celerp 2.3.4__tar.gz → 2.3.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (668) hide show
  1. {celerp-2.3.4 → celerp-2.3.6}/PKG-INFO +1 -1
  2. celerp-2.3.6/celerp/capacity.py +22 -0
  3. {celerp-2.3.4 → celerp-2.3.6}/celerp/db.py +10 -7
  4. {celerp-2.3.4 → celerp-2.3.6}/celerp/events/engine.py +44 -0
  5. {celerp-2.3.4 → celerp-2.3.6}/celerp/events/schemas.py +61 -12
  6. {celerp-2.3.4 → celerp-2.3.6}/celerp/gateway/client.py +172 -53
  7. celerp-2.3.6/celerp/inventory_codes.py +185 -0
  8. {celerp-2.3.4 → celerp-2.3.6}/celerp/main.py +9 -6
  9. {celerp-2.3.4 → celerp-2.3.6}/celerp/middleware.py +2 -2
  10. celerp-2.3.6/celerp/migrations/versions/d1e2f3a4b5c6_rfid_epc_unique_index.py +44 -0
  11. {celerp-2.3.4 → celerp-2.3.6}/celerp/models/projections.py +12 -1
  12. {celerp-2.3.4 → celerp-2.3.6}/celerp/modules/loader.py +232 -9
  13. {celerp-2.3.4 → celerp-2.3.6}/celerp/modules/slots.py +27 -8
  14. {celerp-2.3.4 → celerp-2.3.6}/celerp/notifications/sse.py +1 -36
  15. {celerp-2.3.4 → celerp-2.3.6}/celerp/projections/engine.py +24 -11
  16. {celerp-2.3.4 → celerp-2.3.6}/celerp/routers/auth.py +0 -80
  17. {celerp-2.3.4 → celerp-2.3.6}/celerp/routers/events.py +29 -7
  18. {celerp-2.3.4 → celerp-2.3.6}/celerp/routers/notifications.py +1 -36
  19. celerp-2.3.6/celerp/routers/search.py +305 -0
  20. celerp-2.3.6/celerp/services/document_lines.py +102 -0
  21. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/field_schema.py +2 -0
  22. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/units.py +12 -3
  23. {celerp-2.3.4 → celerp-2.3.6}/celerp.egg-info/PKG-INFO +1 -1
  24. {celerp-2.3.4 → celerp-2.3.6}/celerp.egg-info/SOURCES.txt +12 -0
  25. {celerp-2.3.4 → celerp-2.3.6}/celerp.egg-info/scm_file_list.json +32 -0
  26. celerp-2.3.6/celerp.egg-info/scm_version.json +8 -0
  27. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/__init__.py +5 -0
  28. celerp-2.3.6/default_modules/celerp-accounting/celerp_accounting/search.py +30 -0
  29. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-contacts/__init__.py +5 -0
  30. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-contacts/celerp_contacts/routes.py +6 -24
  31. celerp-2.3.6/default_modules/celerp-contacts/celerp_contacts/search.py +75 -0
  32. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/__init__.py +5 -0
  33. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/celerp_docs/routes.py +190 -35
  34. celerp-2.3.6/default_modules/celerp-docs/celerp_docs/search.py +68 -0
  35. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_audits.py +5 -2
  36. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_line_page_patch_truncate.py +35 -17
  37. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_list_page_no_full_state.py +4 -1
  38. celerp-2.3.6/default_modules/celerp-docs/tests/test_list_quantity_integrity.py +370 -0
  39. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_list_query_pushdown.py +21 -10
  40. celerp-2.3.6/default_modules/celerp-docs/tests/test_list_sell_by_and_reference.py +103 -0
  41. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-inventory/__init__.py +5 -0
  42. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-inventory/celerp_inventory/projections.py +2 -1
  43. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-inventory/celerp_inventory/routes.py +317 -185
  44. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-inventory/celerp_inventory/routes_scanning.py +1 -1
  45. celerp-2.3.6/default_modules/celerp-inventory/celerp_inventory/search.py +265 -0
  46. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-inventory/celerp_inventory/services.py +70 -18
  47. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/__init__.py +5 -0
  48. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/celerp_manufacturing/routes.py +3 -31
  49. celerp-2.3.6/default_modules/celerp-manufacturing/celerp_manufacturing/search.py +74 -0
  50. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_work_centers_and_settings.py +28 -9
  51. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-subscriptions/__init__.py +5 -0
  52. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-subscriptions/celerp_subscriptions/routes.py +4 -41
  53. celerp-2.3.6/default_modules/celerp-subscriptions/celerp_subscriptions/search.py +85 -0
  54. {celerp-2.3.4 → celerp-2.3.6}/default_modules/first_party.lock.json +6 -6
  55. {celerp-2.3.4 → celerp-2.3.6}/ui/api_client.py +636 -159
  56. {celerp-2.3.4 → celerp-2.3.6}/ui/app.py +9 -13
  57. {celerp-2.3.4 → celerp-2.3.6}/ui/components/shell.py +63 -18
  58. {celerp-2.3.4 → celerp-2.3.6}/ui/locales/am.json +10 -2
  59. {celerp-2.3.4 → celerp-2.3.6}/ui/locales/ar.json +10 -2
  60. {celerp-2.3.4 → celerp-2.3.6}/ui/locales/de.json +10 -2
  61. {celerp-2.3.4 → celerp-2.3.6}/ui/locales/en.json +10 -2
  62. {celerp-2.3.4 → celerp-2.3.6}/ui/locales/es.json +10 -2
  63. {celerp-2.3.4 → celerp-2.3.6}/ui/locales/fr.json +10 -2
  64. {celerp-2.3.4 → celerp-2.3.6}/ui/locales/id.json +10 -2
  65. {celerp-2.3.4 → celerp-2.3.6}/ui/locales/it.json +10 -2
  66. {celerp-2.3.4 → celerp-2.3.6}/ui/locales/ja.json +10 -2
  67. {celerp-2.3.4 → celerp-2.3.6}/ui/locales/pt.json +10 -2
  68. {celerp-2.3.4 → celerp-2.3.6}/ui/locales/th.json +10 -2
  69. {celerp-2.3.4 → celerp-2.3.6}/ui/locales/vi.json +10 -2
  70. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/auth.py +8 -53
  71. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/documents.py +139 -20
  72. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/inventory.py +173 -77
  73. celerp-2.3.6/ui/routes/notifications.py +58 -0
  74. celerp-2.3.6/ui/routes/search.py +218 -0
  75. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/settings.py +12 -25
  76. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/stars.py +11 -16
  77. celerp-2.3.6/ui/security.py +44 -0
  78. celerp-2.3.4/celerp/inventory_codes.py +0 -87
  79. celerp-2.3.4/celerp.egg-info/scm_version.json +0 -8
  80. celerp-2.3.4/ui/routes/notifications.py +0 -113
  81. celerp-2.3.4/ui/routes/search.py +0 -170
  82. celerp-2.3.4/ui/security.py +0 -19
  83. {celerp-2.3.4 → celerp-2.3.6}/CLA.md +0 -0
  84. {celerp-2.3.4 → celerp-2.3.6}/CONTRIBUTING.md +0 -0
  85. {celerp-2.3.4 → celerp-2.3.6}/LICENSE +0 -0
  86. {celerp-2.3.4 → celerp-2.3.6}/LICENSING.md +0 -0
  87. {celerp-2.3.4 → celerp-2.3.6}/MANIFEST.in +0 -0
  88. {celerp-2.3.4 → celerp-2.3.6}/NOTICE +0 -0
  89. {celerp-2.3.4 → celerp-2.3.6}/README.md +0 -0
  90. {celerp-2.3.4 → celerp-2.3.6}/alembic.ini +0 -0
  91. {celerp-2.3.4 → celerp-2.3.6}/celerp/__init__.py +0 -0
  92. {celerp-2.3.4 → celerp-2.3.6}/celerp/__main__.py +0 -0
  93. {celerp-2.3.4 → celerp-2.3.6}/celerp/ai/__init__.py +0 -0
  94. {celerp-2.3.4 → celerp-2.3.6}/celerp/ai/batch.py +0 -0
  95. {celerp-2.3.4 → celerp-2.3.6}/celerp/ai/cleanup.py +0 -0
  96. {celerp-2.3.4 → celerp-2.3.6}/celerp/ai/commands.py +0 -0
  97. {celerp-2.3.4 → celerp-2.3.6}/celerp/ai/conversations.py +0 -0
  98. {celerp-2.3.4 → celerp-2.3.6}/celerp/ai/files.py +0 -0
  99. {celerp-2.3.4 → celerp-2.3.6}/celerp/ai/intent.py +0 -0
  100. {celerp-2.3.4 → celerp-2.3.6}/celerp/ai/llm.py +0 -0
  101. {celerp-2.3.4 → celerp-2.3.6}/celerp/ai/memory.py +0 -0
  102. {celerp-2.3.4 → celerp-2.3.6}/celerp/ai/models.py +0 -0
  103. {celerp-2.3.4 → celerp-2.3.6}/celerp/ai/page_count.py +0 -0
  104. {celerp-2.3.4 → celerp-2.3.6}/celerp/ai/quota.py +0 -0
  105. {celerp-2.3.4 → celerp-2.3.6}/celerp/ai/service.py +0 -0
  106. {celerp-2.3.4 → celerp-2.3.6}/celerp/ai/tools.py +0 -0
  107. {celerp-2.3.4 → celerp-2.3.6}/celerp/alembic.ini +0 -0
  108. {celerp-2.3.4 → celerp-2.3.6}/celerp/alembic_config.py +0 -0
  109. {celerp-2.3.4 → celerp-2.3.6}/celerp/cli.py +0 -0
  110. {celerp-2.3.4 → celerp-2.3.6}/celerp/compat.py +0 -0
  111. {celerp-2.3.4 → celerp-2.3.6}/celerp/compute/__init__.py +0 -0
  112. {celerp-2.3.4 → celerp-2.3.6}/celerp/compute/aggregations.py +0 -0
  113. {celerp-2.3.4 → celerp-2.3.6}/celerp/compute/valuation.py +0 -0
  114. {celerp-2.3.4 → celerp-2.3.6}/celerp/config.py +0 -0
  115. {celerp-2.3.4 → celerp-2.3.6}/celerp/connectors/__init__.py +0 -0
  116. {celerp-2.3.4 → celerp-2.3.6}/celerp/connectors/base.py +0 -0
  117. {celerp-2.3.4 → celerp-2.3.6}/celerp/connectors/daily_scheduler.py +0 -0
  118. {celerp-2.3.4 → celerp-2.3.6}/celerp/connectors/http.py +0 -0
  119. {celerp-2.3.4 → celerp-2.3.6}/celerp/connectors/images.py +0 -0
  120. {celerp-2.3.4 → celerp-2.3.6}/celerp/connectors/quickbooks.py +0 -0
  121. {celerp-2.3.4 → celerp-2.3.6}/celerp/connectors/registry.py +0 -0
  122. {celerp-2.3.4 → celerp-2.3.6}/celerp/connectors/relay_token.py +0 -0
  123. {celerp-2.3.4 → celerp-2.3.6}/celerp/connectors/shopify.py +0 -0
  124. {celerp-2.3.4 → celerp-2.3.6}/celerp/connectors/sync_runner.py +0 -0
  125. {celerp-2.3.4 → celerp-2.3.6}/celerp/connectors/upsert.py +0 -0
  126. {celerp-2.3.4 → celerp-2.3.6}/celerp/connectors/util.py +0 -0
  127. {celerp-2.3.4 → celerp-2.3.6}/celerp/connectors/webhooks.py +0 -0
  128. {celerp-2.3.4 → celerp-2.3.6}/celerp/connectors/woocommerce.py +0 -0
  129. {celerp-2.3.4 → celerp-2.3.6}/celerp/connectors/xero.py +0 -0
  130. {celerp-2.3.4 → celerp-2.3.6}/celerp/constants.py +0 -0
  131. {celerp-2.3.4 → celerp-2.3.6}/celerp/embedded_pg.py +0 -0
  132. {celerp-2.3.4 → celerp-2.3.6}/celerp/events/__init__.py +0 -0
  133. {celerp-2.3.4 → celerp-2.3.6}/celerp/events/types.py +0 -0
  134. {celerp-2.3.4 → celerp-2.3.6}/celerp/gateway/__init__.py +0 -0
  135. {celerp-2.3.4 → celerp-2.3.6}/celerp/gateway/state.py +0 -0
  136. {celerp-2.3.4 → celerp-2.3.6}/celerp/importers/__init__.py +0 -0
  137. {celerp-2.3.4 → celerp-2.3.6}/celerp/importers/importer.py +0 -0
  138. {celerp-2.3.4 → celerp-2.3.6}/celerp/importers/schema.py +0 -0
  139. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/README.md +0 -0
  140. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/_auto_stamp.py +0 -0
  141. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/_data_reconcile.py +0 -0
  142. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/_json_compat.py +0 -0
  143. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/env.py +0 -0
  144. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/script.py.mako +0 -0
  145. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/a0b1c2d3e4f5_seed_landed_cost_clearing_accounts.py +0 -0
  146. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/a1b2c3d4e5f6_add_doc_share_tokens.py +0 -0
  147. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/b1c2d3e4f5a6_add_supporter_badges.py +0 -0
  148. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/b2c3d4e5f6a7_add_import_batches.py +0 -0
  149. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/b3c4d5e6f7a8_expand_role_permissions_to_role_grants.py +0 -0
  150. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/bc0d1e2f3a4b_barcode_unique_index.py +0 -0
  151. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/c1d2e3f4a5b6_add_marketplace.py +0 -0
  152. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/c2d3e4f5a6b7_add_is_sync_to_shopify.py +0 -0
  153. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/c4d5e6f7a8b9_account_cash_flow_category.py +0 -0
  154. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/c9d8e7f6a5b4_seed_shrinkage_writeoff_account.py +0 -0
  155. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/d0e1f2a3b4c5_add_share_token_expiry.py +0 -0
  156. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/d2e3f4a5b6c7_add_password_reset_token.py +0 -0
  157. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/d5e6f7a8b9c0_rename_fx_rounding_to_fx_difference.py +0 -0
  158. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/e3f4a5b6c7d8_add_company_is_active.py +0 -0
  159. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/e4f5a6b7c8d9_backfill_connector_idempotency_key.py +0 -0
  160. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/e7c9a1b3d5f2_work_center_default_columns.py +0 -0
  161. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/f1a2b3c4d5e6_move_accounts_marketplace_to_modules.py +0 -0
  162. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/f6a7b8c9d0e1_seed_fx_rounding_account.py +0 -0
  163. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/f8a9b0c1d2e3_work_center_default_backfill.py +0 -0
  164. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/fd5de461e14e_initial_schema.py +0 -0
  165. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/g1h2i3j4k5l6_add_consignment_flag.py +0 -0
  166. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/h2i3j4k5l6m7_add_bank_accounts_and_reconciliation.py +0 -0
  167. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/j4k5l6m7n8o9_labels_db_persistence.py +0 -0
  168. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/k5l6m7n8o9p0_add_reconciliation_v2.py +0 -0
  169. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/l6m7n8o9p0q1_add_sync_runs_table.py +0 -0
  170. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/m7n8o9p0q1r2_add_connector_configs_and_outbound_queue.py +0 -0
  171. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/n8o9p0q1r2s3_backfill_payment_date.py +0 -0
  172. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/o9p0q1r2s3t4_backfill_item_price_fields.py +0 -0
  173. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/p1q2r3s4t5u6_add_session_registry_and_user_auth_state.py +0 -0
  174. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/q2r3s4t5u6v7_add_system_runtime_state.py +0 -0
  175. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/r3s4t5u6v7w8_add_projections_company_entity_type_index.py +0 -0
  176. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/s4t5u6v7w8x9_drop_user_company_id_role.py +0 -0
  177. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/t5u6v7w8x9y0_seed_inventory_sub_accounts.py +0 -0
  178. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/u6v7w8x9y0z1_backfill_item_prices_from_attributes.py +0 -0
  179. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/v7w8x9y0z1a2_backfill_purchase_unit_defaults.py +0 -0
  180. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/w8x9y0z1a2b3_backfill_category_unit_defaults.py +0 -0
  181. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/x7y8z9a0b1c2_add_projection_created_at.py +0 -0
  182. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/y8z9a0b1c2d3_per_company_ledger_idempotency.py +0 -0
  183. {celerp-2.3.4 → celerp-2.3.6}/celerp/migrations/versions/z9a0b1c2d3e4_work_centers.py +0 -0
  184. {celerp-2.3.4 → celerp-2.3.6}/celerp/models/__init__.py +0 -0
  185. {celerp-2.3.4 → celerp-2.3.6}/celerp/models/accounting.py +0 -0
  186. {celerp-2.3.4 → celerp-2.3.6}/celerp/models/ai.py +0 -0
  187. {celerp-2.3.4 → celerp-2.3.6}/celerp/models/auth.py +0 -0
  188. {celerp-2.3.4 → celerp-2.3.6}/celerp/models/base.py +0 -0
  189. {celerp-2.3.4 → celerp-2.3.6}/celerp/models/company.py +0 -0
  190. {celerp-2.3.4 → celerp-2.3.6}/celerp/models/connector_config.py +0 -0
  191. {celerp-2.3.4 → celerp-2.3.6}/celerp/models/import_batch.py +0 -0
  192. {celerp-2.3.4 → celerp-2.3.6}/celerp/models/ledger.py +0 -0
  193. {celerp-2.3.4 → celerp-2.3.6}/celerp/models/marketplace.py +0 -0
  194. {celerp-2.3.4 → celerp-2.3.6}/celerp/models/notification.py +0 -0
  195. {celerp-2.3.4 → celerp-2.3.6}/celerp/models/share.py +0 -0
  196. {celerp-2.3.4 → celerp-2.3.6}/celerp/models/supporter.py +0 -0
  197. {celerp-2.3.4 → celerp-2.3.6}/celerp/models/sync_run.py +0 -0
  198. {celerp-2.3.4 → celerp-2.3.6}/celerp/modules/__init__.py +0 -0
  199. {celerp-2.3.4 → celerp-2.3.6}/celerp/modules/api.py +0 -0
  200. {celerp-2.3.4 → celerp-2.3.6}/celerp/modules/audit.py +0 -0
  201. {celerp-2.3.4 → celerp-2.3.6}/celerp/modules/demotion.py +0 -0
  202. {celerp-2.3.4 → celerp-2.3.6}/celerp/modules/importer.py +0 -0
  203. {celerp-2.3.4 → celerp-2.3.6}/celerp/modules/license.py +0 -0
  204. {celerp-2.3.4 → celerp-2.3.6}/celerp/modules/meta.py +0 -0
  205. {celerp-2.3.4 → celerp-2.3.6}/celerp/modules/migrations_runner.py +0 -0
  206. {celerp-2.3.4 → celerp-2.3.6}/celerp/modules/references.py +0 -0
  207. {celerp-2.3.4 → celerp-2.3.6}/celerp/modules/registry.py +0 -0
  208. {celerp-2.3.4 → celerp-2.3.6}/celerp/notifications/__init__.py +0 -0
  209. {celerp-2.3.4 → celerp-2.3.6}/celerp/notifications/service.py +0 -0
  210. {celerp-2.3.4 → celerp-2.3.6}/celerp/output/__init__.py +0 -0
  211. {celerp-2.3.4 → celerp-2.3.6}/celerp/output/branding.py +0 -0
  212. {celerp-2.3.4 → celerp-2.3.6}/celerp/output/doc_print.py +0 -0
  213. {celerp-2.3.4 → celerp-2.3.6}/celerp/output/pdf.py +0 -0
  214. {celerp-2.3.4 → celerp-2.3.6}/celerp/output/share_render.py +0 -0
  215. {celerp-2.3.4 → celerp-2.3.6}/celerp/projections/__init__.py +0 -0
  216. {celerp-2.3.4 → celerp-2.3.6}/celerp/projections/handlers/__init__.py +0 -0
  217. {celerp-2.3.4 → celerp-2.3.6}/celerp/projections/handlers/marketplace.py +0 -0
  218. {celerp-2.3.4 → celerp-2.3.6}/celerp/projections/handlers/scanning.py +0 -0
  219. {celerp-2.3.4 → celerp-2.3.6}/celerp/projections/handlers/shopify.py +0 -0
  220. {celerp-2.3.4 → celerp-2.3.6}/celerp/projections/handlers/system.py +0 -0
  221. {celerp-2.3.4 → celerp-2.3.6}/celerp/routers/__init__.py +0 -0
  222. {celerp-2.3.4 → celerp-2.3.6}/celerp/routers/companies.py +0 -0
  223. {celerp-2.3.4 → celerp-2.3.6}/celerp/routers/debug.py +0 -0
  224. {celerp-2.3.4 → celerp-2.3.6}/celerp/routers/doctor.py +0 -0
  225. {celerp-2.3.4 → celerp-2.3.6}/celerp/routers/health.py +0 -0
  226. {celerp-2.3.4 → celerp-2.3.6}/celerp/routers/ledger.py +0 -0
  227. {celerp-2.3.4 → celerp-2.3.6}/celerp/routers/stars.py +0 -0
  228. {celerp-2.3.4 → celerp-2.3.6}/celerp/routers/system.py +0 -0
  229. {celerp-2.3.4 → celerp-2.3.6}/celerp/schemas/__init__.py +0 -0
  230. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/__init__.py +0 -0
  231. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/activity_redaction.py +0 -0
  232. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/attachments.py +0 -0
  233. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/auth.py +0 -0
  234. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/auto_je.py +0 -0
  235. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/backup.py +0 -0
  236. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/backup_export.py +0 -0
  237. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/backup_import.py +0 -0
  238. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/backup_repo.py +0 -0
  239. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/backup_scheduler.py +0 -0
  240. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/backup_state.py +0 -0
  241. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/cogs_backfill.py +0 -0
  242. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/cost_visibility.py +0 -0
  243. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/demo.py +0 -0
  244. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/dev_release_guard.py +0 -0
  245. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/email.py +0 -0
  246. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/fulfill.py +0 -0
  247. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/holdings.py +0 -0
  248. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/je_keys.py +0 -0
  249. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/landed_cost.py +0 -0
  250. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/line_measures.py +0 -0
  251. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/list_behavior.py +0 -0
  252. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/money.py +0 -0
  253. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/payments.py +0 -0
  254. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/permissions.py +0 -0
  255. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/pick.py +0 -0
  256. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/pricing.py +0 -0
  257. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/relay_share.py +0 -0
  258. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/reorder.py +0 -0
  259. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/runtime_state.py +0 -0
  260. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/session_tracker.py +0 -0
  261. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/shipping.py +0 -0
  262. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/star_cta.py +0 -0
  263. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/status_doc_backfill.py +0 -0
  264. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/supporter_badge.py +0 -0
  265. {celerp-2.3.4 → celerp-2.3.6}/celerp/services/system_health.py +0 -0
  266. {celerp-2.3.4 → celerp-2.3.6}/celerp/session_gate.py +0 -0
  267. {celerp-2.3.4 → celerp-2.3.6}/celerp/tax_regimes.py +0 -0
  268. {celerp-2.3.4 → celerp-2.3.6}/celerp.egg-info/dependency_links.txt +0 -0
  269. {celerp-2.3.4 → celerp-2.3.6}/celerp.egg-info/entry_points.txt +0 -0
  270. {celerp-2.3.4 → celerp-2.3.6}/celerp.egg-info/requires.txt +0 -0
  271. {celerp-2.3.4 → celerp-2.3.6}/celerp.egg-info/top_level.txt +0 -0
  272. {celerp-2.3.4 → celerp-2.3.6}/conftest.py +0 -0
  273. {celerp-2.3.4 → celerp-2.3.6}/conftest_support.py +0 -0
  274. {celerp-2.3.4 → celerp-2.3.6}/default_modules/__init__.py +0 -0
  275. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/LICENSE +0 -0
  276. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/celerp_accounting/__init__.py +0 -0
  277. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/celerp_accounting/api_setup.py +0 -0
  278. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/celerp_accounting/csv_parser.py +0 -0
  279. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/celerp_accounting/matcher.py +0 -0
  280. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/celerp_accounting/models.py +0 -0
  281. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/celerp_accounting/projections.py +0 -0
  282. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/celerp_accounting/routes.py +0 -0
  283. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/celerp_accounting/ui_routes.py +0 -0
  284. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/report_goldens/balance-sheet.html +0 -0
  285. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/report_goldens/cash-flow.html +0 -0
  286. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/report_goldens/extended-journal-csv.csv +0 -0
  287. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/report_goldens/extended-journal-print.html +0 -0
  288. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/report_goldens/extended-journal.html +0 -0
  289. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/report_goldens/general-ledger.html +0 -0
  290. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/report_goldens/journal-csv.csv +0 -0
  291. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/report_goldens/journal-print.html +0 -0
  292. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/report_goldens/journal.html +0 -0
  293. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/report_goldens/ledger.html +0 -0
  294. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/report_goldens/pnl.html +0 -0
  295. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/report_goldens/statement.html +0 -0
  296. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/report_goldens/trial-balance.html +0 -0
  297. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/test_accounting_banking.py +0 -0
  298. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/test_accounting_banking_routers.py +0 -0
  299. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/test_accounting_gaps.py +0 -0
  300. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/test_accounting_import_idempotency.py +0 -0
  301. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/test_cash_flow.py +0 -0
  302. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/test_financial_reports_ui.py +0 -0
  303. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/test_journal_gl_soa.py +0 -0
  304. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/test_journal_gl_soa_ui.py +0 -0
  305. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/test_reconciliation_v2.py +0 -0
  306. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/test_reports_end_to_end.py +0 -0
  307. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-accounting/tests/test_route_permissions.py +0 -0
  308. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-admin/__init__.py +0 -0
  309. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-admin/celerp_admin/__init__.py +0 -0
  310. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-admin/celerp_admin/routes.py +0 -0
  311. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-admin/celerp_admin/setup.py +0 -0
  312. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/__init__.py +0 -0
  313. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/celerp_ai/__init__.py +0 -0
  314. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/celerp_ai/routes.py +0 -0
  315. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/celerp_ai/setup.py +0 -0
  316. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/celerp_ai/ui_routes.py +0 -0
  317. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/tests/conftest.py +0 -0
  318. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/tests/test_batch.py +0 -0
  319. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/tests/test_cleanup.py +0 -0
  320. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/tests/test_conversations.py +0 -0
  321. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/tests/test_coverage_gaps.py +0 -0
  322. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/tests/test_intent.py +0 -0
  323. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/tests/test_llm.py +0 -0
  324. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/tests/test_memory.py +0 -0
  325. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/tests/test_models.py +0 -0
  326. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/tests/test_page_count.py +0 -0
  327. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/tests/test_quota.py +0 -0
  328. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/tests/test_router.py +0 -0
  329. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/tests/test_routes_coverage.py +0 -0
  330. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/tests/test_service.py +0 -0
  331. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-ai/tests/test_tools.py +0 -0
  332. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-backup/__init__.py +0 -0
  333. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-backup/celerp_backup/__init__.py +0 -0
  334. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-backup/celerp_backup/routes.py +0 -0
  335. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-backup/celerp_backup/setup.py +0 -0
  336. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/__init__.py +0 -0
  337. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/celerp_connectors/__init__.py +0 -0
  338. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/celerp_connectors/models.py +0 -0
  339. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/celerp_connectors/routes.py +0 -0
  340. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/tests/__init__.py +0 -0
  341. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/tests/conftest.py +0 -0
  342. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/tests/test_compliance.py +0 -0
  343. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/tests/test_connector_correctness.py +0 -0
  344. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/tests/test_connectors.py +0 -0
  345. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/tests/test_credentials_routes.py +0 -0
  346. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/tests/test_images.py +0 -0
  347. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/tests/test_outbound.py +0 -0
  348. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/tests/test_quickbooks.py +0 -0
  349. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/tests/test_rate_limiting.py +0 -0
  350. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/tests/test_shopify.py +0 -0
  351. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/tests/test_wiring.py +0 -0
  352. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/tests/test_woocommerce.py +0 -0
  353. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/tests/test_woocommerce_webhooks.py +0 -0
  354. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-connectors/tests/test_xero.py +0 -0
  355. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-contacts/LICENSE +0 -0
  356. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-contacts/celerp_contacts/__init__.py +0 -0
  357. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-contacts/celerp_contacts/migrations.py +0 -0
  358. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-contacts/celerp_contacts/projections.py +0 -0
  359. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-contacts/celerp_contacts/services.py +0 -0
  360. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-contacts/celerp_contacts/ui_routes.py +0 -0
  361. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-dashboard/LICENSE +0 -0
  362. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-dashboard/__init__.py +0 -0
  363. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-dashboard/celerp_dashboard/__init__.py +0 -0
  364. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-dashboard/celerp_dashboard/routes.py +0 -0
  365. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-dashboard/celerp_dashboard/setup.py +0 -0
  366. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/LICENSE +0 -0
  367. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/celerp_docs/__init__.py +0 -0
  368. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/celerp_docs/api_setup.py +0 -0
  369. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/celerp_docs/doc_constants.py +0 -0
  370. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/celerp_docs/doc_email.py +0 -0
  371. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/celerp_docs/doc_projections.py +0 -0
  372. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/celerp_docs/doc_service.py +0 -0
  373. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/celerp_docs/models_share.py +0 -0
  374. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/celerp_docs/routes_payments.py +0 -0
  375. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/celerp_docs/routes_share.py +0 -0
  376. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/celerp_docs/sequences.py +0 -0
  377. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/celerp_docs/taxes.py +0 -0
  378. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/celerp_docs/ui_routes.py +0 -0
  379. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/conftest.py +0 -0
  380. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_bulk_payment_alloc_lock.py +0 -0
  381. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_consolidate_lots.py +0 -0
  382. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_cross_lot_settlement.py +0 -0
  383. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_freight_je.py +0 -0
  384. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_freight_lines.py +0 -0
  385. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_fulfill_no_split.py +0 -0
  386. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_fulfillment_activity.py +0 -0
  387. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_inbound_je_lifecycle.py +0 -0
  388. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_inventory_account_reconciliation.py +0 -0
  389. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_landed_receive.py +0 -0
  390. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_line_delete_persist.py +0 -0
  391. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_line_identifier.py +0 -0
  392. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_list_concurrency.py +0 -0
  393. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_list_outcome_concurrency.py +0 -0
  394. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_memo_close.py +0 -0
  395. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_memo_close_projection.py +0 -0
  396. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_memo_close_review_fixes.py +0 -0
  397. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_memo_lifecycle_concurrency.py +0 -0
  398. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_memo_line_labels.py +0 -0
  399. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_memo_unfulfilled_filter.py +0 -0
  400. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_payment_void_activity.py +0 -0
  401. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_receive_sku_batch.py +0 -0
  402. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_send_visibility.py +0 -0
  403. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_service_lines.py +0 -0
  404. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_sold_label_ordering.py +0 -0
  405. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_void_guards.py +0 -0
  406. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-docs/tests/test_writeoffs.py +0 -0
  407. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-inventory/LICENSE +0 -0
  408. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-inventory/celerp_inventory/__init__.py +0 -0
  409. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-inventory/celerp_inventory/models_import_batch.py +0 -0
  410. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-inventory/celerp_inventory/routes_attachments.py +0 -0
  411. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-inventory/celerp_inventory/ui_routes.py +0 -0
  412. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-inventory/tests/test_item_recipe_projection.py +0 -0
  413. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-inventory/tests/test_item_workflow_projection.py +0 -0
  414. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-inventory/tests/test_landed_cost_projection.py +0 -0
  415. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/LICENSE +0 -0
  416. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/__init__.py +0 -0
  417. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/celerp_labels/__init__.py +0 -0
  418. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/celerp_labels/locales/am.json +0 -0
  419. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/celerp_labels/locales/ar.json +0 -0
  420. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/celerp_labels/locales/de.json +0 -0
  421. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/celerp_labels/locales/en.json +0 -0
  422. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/celerp_labels/locales/es.json +0 -0
  423. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/celerp_labels/locales/fr.json +0 -0
  424. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/celerp_labels/locales/id.json +0 -0
  425. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/celerp_labels/locales/it.json +0 -0
  426. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/celerp_labels/locales/ja.json +0 -0
  427. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/celerp_labels/locales/pt.json +0 -0
  428. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/celerp_labels/locales/th.json +0 -0
  429. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/celerp_labels/locales/vi.json +0 -0
  430. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/celerp_labels/models.py +0 -0
  431. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/celerp_labels/presets.py +0 -0
  432. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/celerp_labels/routes.py +0 -0
  433. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/celerp_labels/service.py +0 -0
  434. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/celerp_labels/ui_routes.py +0 -0
  435. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/requirements.txt +0 -0
  436. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/tests/test_labels.py +0 -0
  437. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-labels/tests/test_labels_module.py +0 -0
  438. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/LICENSE +0 -0
  439. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/celerp_manufacturing/__init__.py +0 -0
  440. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/celerp_manufacturing/costing.py +0 -0
  441. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/celerp_manufacturing/expansion.py +0 -0
  442. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/celerp_manufacturing/labor.py +0 -0
  443. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/celerp_manufacturing/migrations.py +0 -0
  444. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/celerp_manufacturing/projection_handler.py +0 -0
  445. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/celerp_manufacturing/ui_routes.py +0 -0
  446. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/requirements.txt +0 -0
  447. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_auto_complete_on_finalize.py +0 -0
  448. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_bom_removed.py +0 -0
  449. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_build_api.py +0 -0
  450. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_completion_lock_order.py +0 -0
  451. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_item_recipe_api.py +0 -0
  452. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_item_workflow_api.py +0 -0
  453. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_labor_providers.py +0 -0
  454. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_manufacture_from_doc.py +0 -0
  455. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_manufacturing.py +0 -0
  456. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_manufacturing_import_idempotency.py +0 -0
  457. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_manufacturing_module.py +0 -0
  458. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_mfg_events.py +0 -0
  459. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_phase2_manufacturing_validations.py +0 -0
  460. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_production_order.py +0 -0
  461. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_recipe_costing.py +0 -0
  462. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_recipe_expansion.py +0 -0
  463. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_recost_api.py +0 -0
  464. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_run_records.py +0 -0
  465. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_to_make_allocation.py +0 -0
  466. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_where_used.py +0 -0
  467. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-manufacturing/tests/test_workflow_time.py +0 -0
  468. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-reports/LICENSE +0 -0
  469. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-reports/__init__.py +0 -0
  470. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-reports/celerp_reports/__init__.py +0 -0
  471. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-reports/celerp_reports/api_setup.py +0 -0
  472. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-reports/celerp_reports/routes.py +0 -0
  473. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-reports/celerp_reports/ui_routes.py +0 -0
  474. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-reports/tests/test_reports_full.py +0 -0
  475. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-reports/tests/test_reports_unit.py +0 -0
  476. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-subscriptions/LICENSE +0 -0
  477. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-subscriptions/celerp_subscriptions/__init__.py +0 -0
  478. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-subscriptions/celerp_subscriptions/ui_routes.py +0 -0
  479. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-subscriptions/tests/test_subscriptions.py +0 -0
  480. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-subscriptions/tests/test_subscriptions_ui.py +0 -0
  481. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/LICENSE +0 -0
  482. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/__init__.py +0 -0
  483. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/__init__.py +0 -0
  484. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/accessory_fashion.json +0 -0
  485. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/activewear.json +0 -0
  486. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/art_photography.json +0 -0
  487. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/audio_equipment.json +0 -0
  488. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/bag_handbag.json +0 -0
  489. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/banknote.json +0 -0
  490. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/beauty_tool.json +0 -0
  491. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/bed_bedroom.json +0 -0
  492. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/beer.json +0 -0
  493. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/beverage_nonalc.json +0 -0
  494. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/body_part_auto.json +0 -0
  495. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/book.json +0 -0
  496. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/bottoms.json +0 -0
  497. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/brake_suspension.json +0 -0
  498. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/bullion_coin.json +0 -0
  499. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/camera.json +0 -0
  500. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/colored_stone.json +0 -0
  501. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/comic.json +0 -0
  502. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/commercial_space.json +0 -0
  503. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/component_part.json +0 -0
  504. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/confectionery.json +0 -0
  505. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/consulting_service.json +0 -0
  506. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/decorative_art.json +0 -0
  507. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/diamond.json +0 -0
  508. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/drawing.json +0 -0
  509. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/dress_jumpsuit.json +0 -0
  510. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/electrical_auto.json +0 -0
  511. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/electrical_component.json +0 -0
  512. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/emerald.json +0 -0
  513. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/engine_part.json +0 -0
  514. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/fastener.json +0 -0
  515. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/fertilizer_chemical.json +0 -0
  516. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/film_video.json +0 -0
  517. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/fine_writing_instrument.json +0 -0
  518. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/fluid_lubricant.json +0 -0
  519. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/footwear.json +0 -0
  520. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/fragrance.json +0 -0
  521. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/fresh_food.json +0 -0
  522. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/fresh_produce.json +0 -0
  523. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/frozen_food.json +0 -0
  524. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/gaming_console.json +0 -0
  525. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/gold_bullion.json +0 -0
  526. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/grain_cereal.json +0 -0
  527. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/haircare.json +0 -0
  528. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/hand_tool.json +0 -0
  529. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/ingredient_bulk.json +0 -0
  530. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/interior_part.json +0 -0
  531. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/jewelry.json +0 -0
  532. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/kitchen_dining.json +0 -0
  533. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/laptop.json +0 -0
  534. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/lighting.json +0 -0
  535. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/livestock_feed.json +0 -0
  536. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/makeup.json +0 -0
  537. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/measuring_instrument.json +0 -0
  538. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/medal_token.json +0 -0
  539. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/mineral_specimen.json +0 -0
  540. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/mobile_phone.json +0 -0
  541. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/music_cd.json +0 -0
  542. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/music_vinyl.json +0 -0
  543. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/nail.json +0 -0
  544. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/numismatic_coin.json +0 -0
  545. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/outdoor_furniture.json +0 -0
  546. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/outerwear.json +0 -0
  547. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/packaged_food.json +0 -0
  548. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/painting.json +0 -0
  549. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/parking_bay.json +0 -0
  550. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/pearl.json +0 -0
  551. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/personal_care.json +0 -0
  552. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/pipe_plumbing.json +0 -0
  553. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/platinum_bullion.json +0 -0
  554. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/power_tool.json +0 -0
  555. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/print_edition.json +0 -0
  556. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/residential_unit.json +0 -0
  557. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/rough_gemstone.json +0 -0
  558. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/ruby.json +0 -0
  559. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/saas_plan.json +0 -0
  560. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/safety_ppe.json +0 -0
  561. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/sapphire.json +0 -0
  562. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/sculpture.json +0 -0
  563. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/seating.json +0 -0
  564. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/seeds.json +0 -0
  565. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/silver_bullion.json +0 -0
  566. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/skincare.json +0 -0
  567. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/soft_furnishing.json +0 -0
  568. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/software_addon.json +0 -0
  569. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/software_license.json +0 -0
  570. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/spirit.json +0 -0
  571. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/storage_furniture.json +0 -0
  572. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/swimwear.json +0 -0
  573. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/table_desk.json +0 -0
  574. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/tablet.json +0 -0
  575. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/tire_wheel.json +0 -0
  576. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/tops.json +0 -0
  577. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/trading_card.json +0 -0
  578. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/tv_display.json +0 -0
  579. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/video_game.json +0 -0
  580. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/watch.json +0 -0
  581. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/watch_strap.json +0 -0
  582. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/categories/wine.json +0 -0
  583. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/presets/agricultural.json +0 -0
  584. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/presets/artwork.json +0 -0
  585. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/presets/automotive.json +0 -0
  586. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/presets/blank.json +0 -0
  587. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/presets/books_media.json +0 -0
  588. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/presets/coins_precious_metals.json +0 -0
  589. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/presets/consulting.json +0 -0
  590. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/presets/cosmetics.json +0 -0
  591. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/presets/electronics.json +0 -0
  592. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/presets/fashion.json +0 -0
  593. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/presets/food_beverage.json +0 -0
  594. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/presets/furniture.json +0 -0
  595. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/presets/gemstones.json +0 -0
  596. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/presets/hardware.json +0 -0
  597. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/presets/property_rental.json +0 -0
  598. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/presets/saas.json +0 -0
  599. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/presets/watches.json +0 -0
  600. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/presets/wine_spirits.json +0 -0
  601. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/routes.py +0 -0
  602. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/celerp_verticals/ui_routes.py +0 -0
  603. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/tests/test_verticals.py +0 -0
  604. {celerp-2.3.4 → celerp-2.3.6}/default_modules/celerp-verticals/tests/test_verticals_routers.py +0 -0
  605. {celerp-2.3.4 → celerp-2.3.6}/legal/OFFICIAL-COMPONENTS.md +0 -0
  606. {celerp-2.3.4 → celerp-2.3.6}/legal/TRADEMARK.md +0 -0
  607. {celerp-2.3.4 → celerp-2.3.6}/packaging/winget/Celerp.Celerp.installer.yaml +0 -0
  608. {celerp-2.3.4 → celerp-2.3.6}/packaging/winget/Celerp.Celerp.locale.en-US.yaml +0 -0
  609. {celerp-2.3.4 → celerp-2.3.6}/packaging/winget/Celerp.Celerp.yaml +0 -0
  610. {celerp-2.3.4 → celerp-2.3.6}/packaging/winget/README.md +0 -0
  611. {celerp-2.3.4 → celerp-2.3.6}/pyproject.toml +0 -0
  612. {celerp-2.3.4 → celerp-2.3.6}/setup.cfg +0 -0
  613. {celerp-2.3.4 → celerp-2.3.6}/test_helpers.py +0 -0
  614. {celerp-2.3.4 → celerp-2.3.6}/ui/__init__.py +0 -0
  615. {celerp-2.3.4 → celerp-2.3.6}/ui/components/__init__.py +0 -0
  616. {celerp-2.3.4 → celerp-2.3.6}/ui/components/activity.py +0 -0
  617. {celerp-2.3.4 → celerp-2.3.6}/ui/components/attrs.py +0 -0
  618. {celerp-2.3.4 → celerp-2.3.6}/ui/components/backup.py +0 -0
  619. {celerp-2.3.4 → celerp-2.3.6}/ui/components/cloud_gate.py +0 -0
  620. {celerp-2.3.4 → celerp-2.3.6}/ui/components/currency.py +0 -0
  621. {celerp-2.3.4 → celerp-2.3.6}/ui/components/files.py +0 -0
  622. {celerp-2.3.4 → celerp-2.3.6}/ui/components/journal.py +0 -0
  623. {celerp-2.3.4 → celerp-2.3.6}/ui/components/notes.py +0 -0
  624. {celerp-2.3.4 → celerp-2.3.6}/ui/components/phone.py +0 -0
  625. {celerp-2.3.4 → celerp-2.3.6}/ui/components/report_kit.py +0 -0
  626. {celerp-2.3.4 → celerp-2.3.6}/ui/components/table.py +0 -0
  627. {celerp-2.3.4 → celerp-2.3.6}/ui/config.py +0 -0
  628. {celerp-2.3.4 → celerp-2.3.6}/ui/i18n.py +0 -0
  629. {celerp-2.3.4 → celerp-2.3.6}/ui/marketplace_catalog.py +0 -0
  630. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/__init__.py +0 -0
  631. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/account.py +0 -0
  632. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/accounting.py +0 -0
  633. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/accounting_import.py +0 -0
  634. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/contacts.py +0 -0
  635. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/csv_import.py +0 -0
  636. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/dashboard.py +0 -0
  637. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/docs_import.py +0 -0
  638. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/events.py +0 -0
  639. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/financial_reports.py +0 -0
  640. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/lists_import.py +0 -0
  641. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/manufacturing.py +0 -0
  642. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/modules_page.py +0 -0
  643. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/reconciliation.py +0 -0
  644. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/reports.py +0 -0
  645. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/settings_accounting.py +0 -0
  646. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/settings_cloud.py +0 -0
  647. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/settings_connectors.py +0 -0
  648. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/settings_contacts.py +0 -0
  649. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/settings_general.py +0 -0
  650. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/settings_import.py +0 -0
  651. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/settings_inventory.py +0 -0
  652. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/settings_manufacturing.py +0 -0
  653. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/settings_payments.py +0 -0
  654. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/settings_purchasing.py +0 -0
  655. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/settings_sales.py +0 -0
  656. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/setup.py +0 -0
  657. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/subscriptions.py +0 -0
  658. {celerp-2.3.4 → celerp-2.3.6}/ui/routes/subscriptions_import.py +0 -0
  659. {celerp-2.3.4 → celerp-2.3.6}/ui/static/app.css +0 -0
  660. {celerp-2.3.4 → celerp-2.3.6}/ui/static/htmx.min.js +0 -0
  661. {celerp-2.3.4 → celerp-2.3.6}/ui/static/icon.png +0 -0
  662. {celerp-2.3.4 → celerp-2.3.6}/ui/static/logo.png +0 -0
  663. {celerp-2.3.4 → celerp-2.3.6}/ui/static/vendor/intl-tel-input/img/flags.webp +0 -0
  664. {celerp-2.3.4 → celerp-2.3.6}/ui/static/vendor/intl-tel-input/img/flags@2x.webp +0 -0
  665. {celerp-2.3.4 → celerp-2.3.6}/ui/static/vendor/intl-tel-input/intlTelInput.min.css +0 -0
  666. {celerp-2.3.4 → celerp-2.3.6}/ui/static/vendor/intl-tel-input/intlTelInput.min.js +0 -0
  667. {celerp-2.3.4 → celerp-2.3.6}/ui/static/vendor/intl-tel-input/intlTelInputWithUtils.min.js +0 -0
  668. {celerp-2.3.4 → celerp-2.3.6}/ui/static/vendor/intl-tel-input/utils.js +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: celerp
3
- Version: 2.3.4
3
+ Version: 2.3.6
4
4
  Summary: Free, self-hosted ERP for small businesses
5
5
  License: Business Source License 1.1
6
6
 
@@ -0,0 +1,22 @@
1
+ # Copyright (c) 2026 Noah Severs
2
+ # SPDX-License-Identifier: BUSL-1.1
3
+ """Request-capacity constants shared by the API pool and the local UI transport.
4
+
5
+ The app launches one uvicorn worker (both `celerp start` and the Electron shell
6
+ run `uvicorn celerp.main:app` without `--workers`), so one API process owns one
7
+ request connection pool. These constants are the single source of truth for that
8
+ pool's size and for how many connections the web UI is allowed to open into that
9
+ process, so the two can never drift apart. The base size is the interactive
10
+ ceiling; the overflow is held back for background work, relay, auth, and bursts.
11
+
12
+ This module has no side effects and imports nothing from the app, so both the
13
+ database engine and the UI transport can read it without an import cycle.
14
+ """
15
+ from __future__ import annotations
16
+
17
+ # Base connections the single API request pool keeps open, and the extra it may
18
+ # open under burst before refusing. The web UI's interactive transport uses the
19
+ # base size as its own connection ceiling, so a page fan-out can never demand
20
+ # more connections than the pool holds for interactive work.
21
+ REQUEST_DB_POOL_SIZE = 10
22
+ REQUEST_DB_MAX_OVERFLOW = 5
@@ -9,6 +9,7 @@ from sqlalchemy import text as _sql_text
9
9
  from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
10
10
  from sqlalchemy.pool import NullPool
11
11
 
12
+ from celerp.capacity import REQUEST_DB_MAX_OVERFLOW, REQUEST_DB_POOL_SIZE
12
13
  from celerp.config import settings
13
14
 
14
15
  # Shared advisory-lock key that serialises schema migrations across every process
@@ -33,11 +34,13 @@ def mask_db_credentials(text: str) -> str:
33
34
  _REQUEST_LOCK_TIMEOUT_MS = "3000"
34
35
  _REQUEST_STATEMENT_TIMEOUT_MS = "30000"
35
36
 
36
- # Pool budget: total_possible = (api_workers * (pool_size + max_overflow))
37
- # + (gui_workers * (gui_pool_size + gui_max_overflow))
38
- # Default workers 2 API + 1 GUI → 2*(10+5) + 1*(5+5) = 40 connections max.
39
- # Postgres default max_connections=100 leaves 60 for migrations, admin tools, etc.
40
- # If you increase worker counts, recalculate this budget before deploying.
37
+ # Pool budget: the app runs one API worker (both `celerp start` and the Electron
38
+ # shell launch uvicorn without --workers), so this single process owns one request
39
+ # pool of REQUEST_DB_POOL_SIZE base connections plus REQUEST_DB_MAX_OVERFLOW under
40
+ # burst. The base size is also the web UI's interactive connection ceiling, kept in
41
+ # lockstep through celerp.capacity so the two never drift. Postgres default
42
+ # max_connections=100 leaves ample headroom for migrations, admin tools, and
43
+ # background jobs beyond this pool.
41
44
  if os.environ.get("CELERP_TEST_NULLPOOL"):
42
45
  # Test mode: a fresh connection per use that closes on return, so a failed
43
46
  # test can't leave a poisoned/locked connection lingering in a pooled
@@ -49,8 +52,8 @@ else:
49
52
  settings.database_url,
50
53
  future=True,
51
54
  pool_pre_ping=True,
52
- pool_size=10,
53
- max_overflow=5,
55
+ pool_size=REQUEST_DB_POOL_SIZE,
56
+ max_overflow=REQUEST_DB_MAX_OVERFLOW,
54
57
  # Bound how long any query may wait on a lock or run, so a stuck query is
55
58
  # cancelled instead of pinning one of the few pooled connections for the
56
59
  # full request lifetime (lock_timeout 3s, statement_timeout 30s, in ms).
@@ -11,7 +11,9 @@ from sqlalchemy.exc import IntegrityError
11
11
 
12
12
  from celerp.events.schemas import EVENT_SCHEMA_MAP
13
13
  from celerp.models.ledger import LedgerEntry
14
+ from celerp.models.projections import Projection
14
15
  from celerp.projections.engine import ProjectionEngine
16
+ from celerp.services.document_lines import assert_document_item_uniqueness
15
17
 
16
18
 
17
19
  def apply_event(state: dict, event: LedgerEntry) -> dict:
@@ -126,9 +128,51 @@ async def emit_event(session, **kwargs) -> LedgerEntry:
126
128
 
127
129
  schema(**kwargs["data"])
128
130
 
131
+ # The schema validates a Pydantic-made copy, so any code canonicalization it applies
132
+ # (rfid_epc -> trimmed upper-case, gtin -> validated form) never reaches the raw dict
133
+ # persisted below. Apply the same normalization to the dict that is actually stored, so
134
+ # the stored value equals what the availability check and the partial unique index
135
+ # compare against - otherwise a case-variant identifier is stored raw and never collides.
136
+ _normalize = getattr(schema, "normalize_for_storage", None)
137
+ if _normalize is not None:
138
+ _normalize(kwargs["data"])
139
+
129
140
  # Enforce period lock
130
141
  await _check_period_lock(session, kwargs.get("company_id"), kwargs.get("data", {}))
131
142
 
143
+ # Enforce physical-item uniqueness on new OUTBOUND doc writes (invoice, memo).
144
+ # Extract the post-change line set by DATA SHAPE so every doc writer (create,
145
+ # patch, shared_import, update, conversion, import) is covered by one rule,
146
+ # keyed on entity_type == "doc" rather than an event list. The doc-type scope
147
+ # lives in assert_document_item_uniqueness beside the invariant; this boundary
148
+ # only resolves the doc_type to hand it. Rebuild/replay applies events via
149
+ # apply_event, never emit_event, so historical events are never re-validated.
150
+ if kwargs.get("entity_type") == "doc":
151
+ data = kwargs.get("data") or {}
152
+ line_set = None
153
+ if isinstance(data.get("line_items"), list):
154
+ line_set = data["line_items"]
155
+ elif isinstance(data.get("fields_changed"), dict):
156
+ changed = data["fields_changed"].get("line_items")
157
+ if isinstance(changed, dict):
158
+ line_set = changed.get("new")
159
+ if line_set is not None:
160
+ # Prefer the event's own doc_type (present on doc.created and any update
161
+ # that carries it - zero query). Otherwise resolve it from the persisted
162
+ # projection, reading state["doc_type"] only when that projection is a doc
163
+ # (the Projection PK is (company_id, entity_id) with no type discriminator,
164
+ # so the entity_type check guards against a same-id non-doc projection).
165
+ doc_type = data.get("doc_type")
166
+ if doc_type is None:
167
+ proj = await session.get(
168
+ Projection, (kwargs.get("company_id"), kwargs.get("entity_id"))
169
+ )
170
+ if proj is not None and proj.entity_type == "doc":
171
+ doc_type = (proj.state or {}).get("doc_type")
172
+ await assert_document_item_uniqueness(
173
+ session, kwargs.get("company_id"), doc_type, line_set
174
+ )
175
+
132
176
  entry = LedgerEntry(**kwargs)
133
177
 
134
178
  try:
@@ -15,6 +15,8 @@ from celerp.inventory_codes import ( # noqa: F401 - re-exported for celerp_inve
15
15
  SKU_COMMA_MESSAGE,
16
16
  reject_comma_sku,
17
17
  validate_barcode,
18
+ validate_gtin,
19
+ validate_rfid_epc,
18
20
  validate_sku,
19
21
  )
20
22
 
@@ -24,23 +26,50 @@ from celerp.inventory_codes import ( # noqa: F401 - re-exported for celerp_inve
24
26
  # -----------------
25
27
 
26
28
 
29
+ def _normalize_item_codes(data: dict) -> None:
30
+ """Validate and canonicalize an item event's identity codes IN PLACE.
31
+
32
+ SKU: no comma (Celerp's OR operator in the SKU/search syntax) and a bounded length.
33
+ Barcode: digits only and a bounded length. GTIN: digits only, standard length; the
34
+ validated value replaces the input so the stored form is canonical. rfid_epc trims
35
+ and upper-cases so the stored value equals what the availability check and the
36
+ partial unique index compare against - a case-variant EPC then collides. This is the
37
+ single normalization used both by the schema validator (on the validated copy) and by
38
+ the event boundary (on the persisted dict), so storage, lookup, and uniqueness agree.
39
+ """
40
+ validate_sku(data.get("sku"))
41
+ validate_barcode(data.get("barcode"))
42
+ if "gtin" in data:
43
+ data["gtin"] = validate_gtin(data.get("gtin"))
44
+ if "rfid_epc" in data:
45
+ data["rfid_epc"] = validate_rfid_epc(data.get("rfid_epc"))
46
+
47
+
27
48
  class _SkuGuard(BaseModel):
28
49
  """Mixin: enforce the SKU and barcode invariants on any item event that sets them.
29
50
 
30
- SKU: no comma (Celerp's OR operator in the SKU/search syntax) and a bounded length.
31
- Barcode: digits only and a bounded length. These are the canonical write-time
32
- predicates; interactive routes wrap them to surface a friendly 422, every other
33
- emitter hits them here at the event boundary (imports, connector upserts, document
34
- receiving). Replay never runs these schemas (the reducer does), so enforcing here
35
- can never brick a historical projection.
51
+ The canonical write-time predicates live in _normalize_item_codes; interactive routes
52
+ wrap them to surface a friendly 422, every other emitter hits them here at the event
53
+ boundary (imports, connector upserts, document receiving). Replay never runs these
54
+ schemas (the reducer does), so enforcing here can never brick a historical projection.
36
55
  """
37
56
 
57
+ @classmethod
58
+ def normalize_for_storage(cls, data: dict) -> None:
59
+ """Canonicalize identity codes on the dict that will be persisted (in place).
60
+
61
+ The schema validator runs against a Pydantic-made copy, so its normalization
62
+ never reaches the raw event data the engine stores. The engine calls this on the
63
+ persisted dict itself so the stored value is the canonical one.
64
+ """
65
+ if isinstance(data, dict):
66
+ _normalize_item_codes(data)
67
+
38
68
  @model_validator(mode="before")
39
69
  @classmethod
40
70
  def _guard_item_codes(cls, data: Any) -> Any:
41
71
  if isinstance(data, dict):
42
- validate_sku(data.get("sku"))
43
- validate_barcode(data.get("barcode"))
72
+ _normalize_item_codes(data)
44
73
  return data
45
74
 
46
75
 
@@ -81,15 +110,35 @@ class ItemSnapshot(_SkuGuard):
81
110
  model_config = {"extra": "allow"} # CIF adapters may pass source-specific fields
82
111
 
83
112
 
113
+ def _normalize_updated_codes(fields_changed: dict) -> None:
114
+ """Validate and canonicalize the new identity-code values of an item.updated in place."""
115
+ if "sku" in fields_changed:
116
+ validate_sku((fields_changed.get("sku") or {}).get("new"))
117
+ if "barcode" in fields_changed:
118
+ validate_barcode((fields_changed.get("barcode") or {}).get("new"))
119
+ if "gtin" in fields_changed:
120
+ change = fields_changed["gtin"] or {}
121
+ change["new"] = validate_gtin(change.get("new"))
122
+ fields_changed["gtin"] = change
123
+ if "rfid_epc" in fields_changed:
124
+ change = fields_changed["rfid_epc"] or {}
125
+ # Canonicalize the new value so the stored/looked-up form matches.
126
+ change["new"] = validate_rfid_epc(change.get("new"))
127
+ fields_changed["rfid_epc"] = change
128
+
129
+
84
130
  class ItemUpdated(BaseModel):
85
131
  fields_changed: dict[str, dict[str, Any]]
86
132
 
133
+ @classmethod
134
+ def normalize_for_storage(cls, data: dict) -> None:
135
+ """Canonicalize the persisted item.updated data's new identity values (in place)."""
136
+ if isinstance(data, dict) and isinstance(data.get("fields_changed"), dict):
137
+ _normalize_updated_codes(data["fields_changed"])
138
+
87
139
  @model_validator(mode="after")
88
140
  def _guard_item_codes(self) -> "ItemUpdated":
89
- if "sku" in self.fields_changed:
90
- validate_sku((self.fields_changed.get("sku") or {}).get("new"))
91
- if "barcode" in self.fields_changed:
92
- validate_barcode((self.fields_changed.get("barcode") or {}).get("new"))
141
+ _normalize_updated_codes(self.fields_changed)
93
142
  return self
94
143
 
95
144
 
@@ -20,7 +20,9 @@ import os
20
20
  import time
21
21
  import uuid
22
22
  from typing import Any
23
+ from urllib.parse import unquote
23
24
 
25
+ import httpx
24
26
  import websockets
25
27
  from websockets.exceptions import ConnectionClosed
26
28
 
@@ -42,6 +44,31 @@ _REAP_GRACE = 5 * 60 # seconds
42
44
  _SEND_DEADLINE = 30 # seconds
43
45
 
44
46
 
47
+ class _SharedProxyTransport(httpx.AsyncHTTPTransport):
48
+ """A shared bounded transport reused by a fresh AsyncClient per proxied request.
49
+
50
+ Each proxied browser request gets its own cheap AsyncClient so no cookie state
51
+ carries between visitors, but they all drive requests through this one
52
+ transport, whose Limits cap how many connections the gateway opens into the
53
+ local app. httpx.AsyncClient.__aexit__ (and aclose) unconditionally closes its
54
+ transport, so a per-request client leaving its `async with` block would tear
55
+ the shared pool out from under a sibling request. The per-client close is
56
+ therefore a no-op here: the pool outlives every client and is torn down only
57
+ by shutdown_pool(), called once from GatewayClient.close(). This mirrors the
58
+ UI's own shared-transport pattern; a local copy avoids the gateway layer
59
+ depending on the UI layer.
60
+ """
61
+
62
+ async def aclose(self) -> None:
63
+ return None
64
+
65
+ async def __aexit__(self, *exc_info) -> None:
66
+ return None
67
+
68
+ async def shutdown_pool(self) -> None:
69
+ await super().aclose()
70
+
71
+
45
72
  def _shop_key(handle: str | None) -> str:
46
73
  """Normalize a Shopify store handle/shop domain for comparison (strip scheme,
47
74
  trailing slash, and the .myshopify.com suffix), so a token's `store_handle`
@@ -95,10 +122,12 @@ class GatewayClient:
95
122
  # request that outlived its socket can be recognised as stale and dropped
96
123
  # rather than sent on the freshly rebuilt connection.
97
124
  self._generation: int = 0
98
- # One shared httpx client for all proxied requests, built lazily with bounded
99
- # connection limits. A fresh client per request opened an unbounded number of
100
- # pools under load; one bounded client caps the concurrency into the local app.
101
- self._http_client: Any = None
125
+ # One shared bounded transport for all proxied requests, built lazily. Each
126
+ # proxied request opens its own cheap AsyncClient over this transport, so no
127
+ # cookie state carries between visitors, while the transport's Limits cap how
128
+ # many connections the gateway opens into the local app (a fresh full client
129
+ # per request opened an unbounded number of pools under load).
130
+ self._http_transport: _SharedProxyTransport | None = None
102
131
  # Observability counters for the proxy path, surfaced when the connection ends.
103
132
  self._proxied_count = 0
104
133
  self._cancelled_count = 0
@@ -233,7 +262,7 @@ class GatewayClient:
233
262
  await ws.close()
234
263
  except Exception:
235
264
  pass
236
- await self._close_http_client()
265
+ await self._close_http_transport()
237
266
 
238
267
  async def _should_reap(self) -> bool:
239
268
  """A free instance with no live share and no recent proxied request should
@@ -596,31 +625,64 @@ class GatewayClient:
596
625
  except Exception as exc:
597
626
  log.warning("invoice.payment handling failed (entity=%s): %s", entity_id, exc)
598
627
 
599
- def _get_http_client(self):
600
- """Return the one shared httpx client used for every proxied request, building
601
- it lazily on first use. A bounded connection pool caps how many concurrent
602
- requests this client can drive into the local app, so a burst of relayed
603
- requests can no longer open an unbounded number of pools. The default timeout
604
- is the fixed cap; a per-request timeout_ms tightens it further."""
605
- import httpx
606
-
607
- if self._http_client is None:
608
- self._http_client = httpx.AsyncClient(
609
- timeout=180.0,
628
+ def _get_http_transport(self) -> _SharedProxyTransport:
629
+ """Return the one shared bounded transport that every per-request proxy client
630
+ drives, building it lazily on first use. The bounded connection pool caps how
631
+ many concurrent requests the gateway can drive into the local app, so a burst
632
+ of relayed requests can no longer open an unbounded number of pools. The
633
+ per-request AsyncClient wrapper carries the timeout (the fixed cap, tightened
634
+ further by a per-request timeout_ms)."""
635
+ if self._http_transport is None:
636
+ self._http_transport = _SharedProxyTransport(
610
637
  limits=httpx.Limits(max_connections=32, max_keepalive_connections=8),
611
638
  )
612
- return self._http_client
613
-
614
- async def _close_http_client(self) -> None:
615
- """Close the shared httpx client if one was built. Safe to call when none was."""
616
- client = self._http_client
617
- self._http_client = None
618
- if client is not None:
639
+ return self._http_transport
640
+
641
+ async def _close_http_transport(self) -> None:
642
+ """Shut the shared transport's pool down if one was built. Safe to call when
643
+ none was; nulling first makes a second close() a no-op, so the pool is torn
644
+ down exactly once."""
645
+ transport = self._http_transport
646
+ self._http_transport = None
647
+ if transport is not None:
619
648
  try:
620
- await client.aclose()
649
+ await transport.shutdown_pool()
621
650
  except Exception:
622
651
  pass
623
652
 
653
+ @staticmethod
654
+ def _policy_path(raw_path: Any) -> str | None:
655
+ """Return the canonical local route a relay-supplied path resolves to.
656
+
657
+ httpx removes dot segments ('/x/../settings' -> '/settings') before it puts
658
+ the request on the wire, the local ASGI server then splits the target on the
659
+ first '?', percent-decodes the path once, and routes on the result; a browser
660
+ never sends a fragment. Classification and the local-only guard must run on
661
+ that same canonical value, not the raw wire string, or an encoded
662
+ ('/settings/%66actory-reset'), dot-segment ('/x/../settings/factory-reset'),
663
+ or fragmented ('/settings/factory-reset#x') variant would slip past a raw
664
+ match yet still route to the blocked handler locally. The canonical path is
665
+ derived from httpx itself so it can never diverge from what httpx transmits.
666
+ Returns None for any path that cannot address a local route (non-string,
667
+ non-absolute, carrying a fragment or control character, or not decodable);
668
+ the caller refuses those with a neutral 400.
669
+ """
670
+ if not isinstance(raw_path, str) or not raw_path.startswith("/"):
671
+ return None
672
+ if "#" in raw_path or any(ord(c) < 0x20 or ord(c) == 0x7f for c in raw_path):
673
+ return None
674
+ path_part = raw_path.split("?", 1)[0]
675
+ try:
676
+ transmitted = httpx.URL("http://127.0.0.1" + path_part).raw_path.split(b"?", 1)[0]
677
+ decoded = unquote(transmitted.decode("ascii"), errors="strict")
678
+ except (UnicodeDecodeError, ValueError, httpx.InvalidURL):
679
+ return None
680
+ if not decoded.startswith("/"):
681
+ return None
682
+ if "#" in decoded or any(ord(c) < 0x20 or ord(c) == 0x7f for c in decoded):
683
+ return None
684
+ return decoded
685
+
624
686
  async def _handle_proxy_request(self, payload: dict) -> None:
625
687
  """Handle a proxied HTTP request from the relay.
626
688
 
@@ -644,11 +706,10 @@ class GatewayClient:
644
706
 
645
707
  request_id = payload.get("id", "")
646
708
  method = payload.get("method", "GET")
647
- path = payload.get("path", "/")
709
+ raw_path = payload.get("path")
648
710
  query = payload.get("query", "")
649
711
  headers = payload.get("headers", {})
650
712
  body_b64 = payload.get("body_b64", "")
651
- body = base64.b64decode(body_b64) if body_b64 else None
652
713
  # Additive per-request deadline: a numeric timeout_ms bounds this request below
653
714
  # the fixed cap so a single slow local handler can't pin a relay slot for 180s.
654
715
  # Absent or non-numeric leaves the default cap in force.
@@ -657,17 +718,61 @@ class GatewayClient:
657
718
  if isinstance(timeout_ms, (int, float)) and not isinstance(timeout_ms, bool) and timeout_ms > 0:
658
719
  deadline_s = timeout_ms / 1000.0
659
720
 
721
+ # Validate the relay-supplied path before decoding the body or any
722
+ # classification, port selection, or local forwarding. Classification and
723
+ # the local-only guard run on the CANONICAL path the local server would
724
+ # route to (percent-decoded once, fragment stripped), so an encoded or
725
+ # fragmented variant of a blocked route cannot slip past a raw string
726
+ # match. A path that cannot address a local route is refused with a neutral
727
+ # 400 that echoes nothing back to the caller.
728
+ policy_path = self._policy_path(raw_path)
729
+ if policy_path is None:
730
+ await self._send(self._ws, {
731
+ "type": "http.response",
732
+ "payload": {
733
+ "id": request_id,
734
+ "status": 400,
735
+ "headers": [["content-type", "text/plain"]],
736
+ "body_b64": base64.b64encode(b"Invalid request path").decode(),
737
+ },
738
+ })
739
+ return
740
+
741
+ # Decode the body only after the path is known good. A malformed base64
742
+ # payload is contained with a neutral 400 rather than raising. validate=True
743
+ # rejects non-alphabet characters outright: the permissive default silently
744
+ # drops them, turning a malformed relay payload into a different request body
745
+ # instead of the intended containment.
746
+ try:
747
+ body = base64.b64decode(body_b64, validate=True) if body_b64 else None
748
+ except (ValueError, TypeError):
749
+ await self._send(self._ws, {
750
+ "type": "http.response",
751
+ "payload": {
752
+ "id": request_id,
753
+ "status": 400,
754
+ "headers": [["content-type", "text/plain"]],
755
+ "body_b64": base64.b64encode(b"Invalid request body").decode(),
756
+ },
757
+ })
758
+ return
759
+
660
760
  # SSE / long-poll paths cannot be proxied over the WS request/response
661
- # protocol. Return an empty stream so the browser doesn't 500.
662
- _streaming_paths = ("/notifications/stream",)
663
- if any(path == p or path.startswith(p) for p in _streaming_paths):
761
+ # protocol. Answer with an immediate stub carrying a long retry: directive
762
+ # so the browser's EventSource backs off instead of reconnecting in a tight
763
+ # loop; real-time updates only work on direct local access. Mirrors the
764
+ # relay's own SSE suppression stub.
765
+ _streaming_paths = ("/events/stream",)
766
+ if any(policy_path == p or policy_path.startswith(p + "/") for p in _streaming_paths):
664
767
  await self._send(self._ws, {
665
768
  "type": "http.response",
666
769
  "payload": {
667
770
  "id": request_id,
668
771
  "status": 200,
669
772
  "headers": [["content-type", "text/event-stream"], ["cache-control", "no-cache"]],
670
- "body_b64": base64.b64encode(b"data: {}\n\n").decode(),
773
+ "body_b64": base64.b64encode(
774
+ b"retry: 3600000\n: sse not supported over relay\n\n"
775
+ ).decode(),
671
776
  },
672
777
  })
673
778
  return
@@ -678,7 +783,7 @@ class GatewayClient:
678
783
  # setup is intentionally NOT blocked here - a headless cloud instance is provisioned
679
784
  # through this same proxy, so blocking it would break cloud onboarding.
680
785
  _local_only_paths = ("/settings/factory-reset",)
681
- if any(path == p or path.startswith(p + "/") or path.startswith(p + "?") for p in _local_only_paths):
786
+ if any(policy_path == p or policy_path.startswith(p + "/") for p in _local_only_paths):
682
787
  await self._send(self._ws, {
683
788
  "type": "http.response",
684
789
  "payload": {
@@ -692,35 +797,49 @@ class GatewayClient:
692
797
  })
693
798
  return
694
799
 
695
- port = self._local_port_for(path)
800
+ # Port is chosen from the canonical path (an encoded variant must map to
801
+ # the same local app), but the original path is forwarded verbatim so the
802
+ # local server performs its own decoding.
803
+ port = self._local_port_for(policy_path)
696
804
 
697
- url = f"http://127.0.0.1:{port}{path}"
805
+ url = f"http://127.0.0.1:{port}{raw_path}"
698
806
  if query:
699
807
  url = f"{url}?{query}"
700
808
 
701
- client = self._get_http_client()
702
809
  try:
703
- if deadline_s is not None:
704
- # asyncio.wait_for, not asyncio.timeout: the latter is 3.11+, and this package
705
- # supports Python >=3.10 where it does not exist. wait_for cancels the request and
706
- # raises TimeoutError on the deadline, which the handler below turns into an error
707
- # response.
708
- resp = await asyncio.wait_for(
709
- client.request(
810
+ # A fresh client per request over the shared bounded transport: the
811
+ # wrapper's cookie jar exists only for this one request and is discarded
812
+ # on block exit, so no auth state carries between visitors, while the
813
+ # transport's pool stays shared and bounded. follow_redirects=False is
814
+ # explicit so the local app's redirect and cookies go back to the real
815
+ # browser rather than being followed internally. Opened inside the try so
816
+ # any construction error routes to the except Exception -> 502 below.
817
+ async with httpx.AsyncClient(
818
+ transport=self._get_http_transport(),
819
+ timeout=180.0,
820
+ follow_redirects=False,
821
+ ) as client:
822
+ if deadline_s is not None:
823
+ # asyncio.wait_for, not asyncio.timeout: the latter is 3.11+, and this package
824
+ # supports Python >=3.10 where it does not exist. wait_for cancels the request and
825
+ # raises TimeoutError on the deadline, which the handler below turns into an error
826
+ # response.
827
+ resp = await asyncio.wait_for(
828
+ client.request(
829
+ method=method,
830
+ url=url,
831
+ headers=headers,
832
+ content=body,
833
+ ),
834
+ deadline_s,
835
+ )
836
+ else:
837
+ resp = await client.request(
710
838
  method=method,
711
839
  url=url,
712
840
  headers=headers,
713
841
  content=body,
714
- ),
715
- deadline_s,
716
- )
717
- else:
718
- resp = await client.request(
719
- method=method,
720
- url=url,
721
- headers=headers,
722
- content=body,
723
- )
842
+ )
724
843
  except asyncio.CancelledError:
725
844
  # http.cancel aborted this request: emit nothing and let the cancellation
726
845
  # propagate so the task is recorded cancelled.
@@ -730,7 +849,7 @@ class GatewayClient:
730
849
  # The per-request deadline elapsed before the local handler answered. Surface
731
850
  # it as an error response instead of hanging to the fixed cap.
732
851
  self._timeout_count += 1
733
- log.warning("Proxy request timed out after %sms for %s %s", timeout_ms, method, path)
852
+ log.warning("Proxy request timed out after %sms for %s %s", timeout_ms, method, raw_path)
734
853
  if generation == self._generation:
735
854
  await self._send(self._ws, {
736
855
  "type": "http.response",
@@ -745,7 +864,7 @@ class GatewayClient:
745
864
  self._stale_dropped_count += 1
746
865
  return
747
866
  except Exception as exc:
748
- log.warning("Proxy request failed for %s %s: %s", method, path, exc)
867
+ log.warning("Proxy request failed for %s %s: %s", method, raw_path, exc)
749
868
  if generation == self._generation:
750
869
  await self._send(self._ws, {
751
870
  "type": "http.response",