celerp 1.2.6__tar.gz → 1.2.7__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 (556) hide show
  1. {celerp-1.2.6 → celerp-1.2.7}/CONTRIBUTING.md +4 -5
  2. {celerp-1.2.6 → celerp-1.2.7}/PKG-INFO +1 -1
  3. {celerp-1.2.6 → celerp-1.2.7}/celerp/cli.py +19 -9
  4. {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/quickbooks.py +9 -7
  5. {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/shopify.py +8 -18
  6. {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/sync_runner.py +31 -0
  7. {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/upsert.py +22 -17
  8. celerp-1.2.7/celerp/connectors/util.py +19 -0
  9. {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/webhooks.py +46 -0
  10. {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/woocommerce.py +44 -29
  11. {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/xero.py +18 -13
  12. {celerp-1.2.6 → celerp-1.2.7}/celerp/gateway/client.py +19 -0
  13. celerp-1.2.7/celerp/migrations/_json_compat.py +84 -0
  14. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/n8o9p0q1r2s3_backfill_payment_date.py +33 -34
  15. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/o9p0q1r2s3t4_backfill_item_price_fields.py +17 -17
  16. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/u6v7w8x9y0z1_backfill_item_prices_from_attributes.py +16 -25
  17. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/v7w8x9y0z1a2_backfill_purchase_unit_defaults.py +19 -17
  18. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/w8x9y0z1a2b3_backfill_category_unit_defaults.py +32 -33
  19. {celerp-1.2.6 → celerp-1.2.7}/celerp.egg-info/PKG-INFO +1 -1
  20. {celerp-1.2.6 → celerp-1.2.7}/celerp.egg-info/SOURCES.txt +4 -1
  21. {celerp-1.2.6 → celerp-1.2.7}/celerp.egg-info/scm_file_list.json +10 -1
  22. celerp-1.2.7/celerp.egg-info/scm_version.json +8 -0
  23. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/conftest.py +1 -1
  24. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_compliance.py +3 -2
  25. celerp-1.2.7/default_modules/celerp-connectors/tests/test_connector_correctness.py +226 -0
  26. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_connectors.py +2 -10
  27. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_outbound.py +4 -4
  28. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_quickbooks.py +4 -4
  29. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_wiring.py +119 -1
  30. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_woocommerce.py +51 -0
  31. celerp-1.2.7/default_modules/celerp-connectors/tests/test_woocommerce_webhooks.py +114 -0
  32. celerp-1.2.7/default_modules/celerp-contacts/celerp_contacts/services.py +181 -0
  33. celerp-1.2.7/default_modules/celerp-docs/celerp_docs/doc_service.py +316 -0
  34. celerp-1.2.7/default_modules/celerp-inventory/celerp_inventory/services.py +141 -0
  35. {celerp-1.2.6 → celerp-1.2.7}/pyproject.toml +4 -1
  36. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_connectors.py +78 -2
  37. celerp-1.2.6/celerp/connectors/outbound_queue.py +0 -207
  38. celerp-1.2.6/celerp.egg-info/scm_version.json +0 -8
  39. celerp-1.2.6/default_modules/celerp-contacts/celerp_contacts/services.py +0 -84
  40. celerp-1.2.6/default_modules/celerp-docs/celerp_docs/doc_service.py +0 -80
  41. celerp-1.2.6/default_modules/celerp-inventory/celerp_inventory/services.py +0 -80
  42. {celerp-1.2.6 → celerp-1.2.7}/CLA.md +0 -0
  43. {celerp-1.2.6 → celerp-1.2.7}/LICENSE +0 -0
  44. {celerp-1.2.6 → celerp-1.2.7}/LICENSING.md +0 -0
  45. {celerp-1.2.6 → celerp-1.2.7}/MANIFEST.in +0 -0
  46. {celerp-1.2.6 → celerp-1.2.7}/NOTICE +0 -0
  47. {celerp-1.2.6 → celerp-1.2.7}/README.md +0 -0
  48. {celerp-1.2.6 → celerp-1.2.7}/alembic.ini +0 -0
  49. {celerp-1.2.6 → celerp-1.2.7}/celerp/__init__.py +0 -0
  50. {celerp-1.2.6 → celerp-1.2.7}/celerp/__main__.py +0 -0
  51. {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/__init__.py +0 -0
  52. {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/batch.py +0 -0
  53. {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/cleanup.py +0 -0
  54. {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/commands.py +0 -0
  55. {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/conversations.py +0 -0
  56. {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/files.py +0 -0
  57. {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/intent.py +0 -0
  58. {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/llm.py +0 -0
  59. {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/memory.py +0 -0
  60. {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/models.py +0 -0
  61. {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/page_count.py +0 -0
  62. {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/quota.py +0 -0
  63. {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/service.py +0 -0
  64. {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/tools.py +0 -0
  65. {celerp-1.2.6 → celerp-1.2.7}/celerp/alembic.ini +0 -0
  66. {celerp-1.2.6 → celerp-1.2.7}/celerp/alembic_config.py +0 -0
  67. {celerp-1.2.6 → celerp-1.2.7}/celerp/compute/__init__.py +0 -0
  68. {celerp-1.2.6 → celerp-1.2.7}/celerp/compute/aggregations.py +0 -0
  69. {celerp-1.2.6 → celerp-1.2.7}/celerp/compute/valuation.py +0 -0
  70. {celerp-1.2.6 → celerp-1.2.7}/celerp/config.py +0 -0
  71. {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/__init__.py +0 -0
  72. {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/base.py +0 -0
  73. {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/daily_scheduler.py +0 -0
  74. {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/http.py +0 -0
  75. {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/images.py +0 -0
  76. {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/registry.py +0 -0
  77. {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/relay_token.py +0 -0
  78. {celerp-1.2.6 → celerp-1.2.7}/celerp/constants.py +0 -0
  79. {celerp-1.2.6 → celerp-1.2.7}/celerp/db.py +0 -0
  80. {celerp-1.2.6 → celerp-1.2.7}/celerp/events/__init__.py +0 -0
  81. {celerp-1.2.6 → celerp-1.2.7}/celerp/events/engine.py +0 -0
  82. {celerp-1.2.6 → celerp-1.2.7}/celerp/events/schemas.py +0 -0
  83. {celerp-1.2.6 → celerp-1.2.7}/celerp/events/types.py +0 -0
  84. {celerp-1.2.6 → celerp-1.2.7}/celerp/gateway/__init__.py +0 -0
  85. {celerp-1.2.6 → celerp-1.2.7}/celerp/gateway/state.py +0 -0
  86. {celerp-1.2.6 → celerp-1.2.7}/celerp/importers/__init__.py +0 -0
  87. {celerp-1.2.6 → celerp-1.2.7}/celerp/importers/importer.py +0 -0
  88. {celerp-1.2.6 → celerp-1.2.7}/celerp/importers/schema.py +0 -0
  89. {celerp-1.2.6 → celerp-1.2.7}/celerp/main.py +0 -0
  90. {celerp-1.2.6 → celerp-1.2.7}/celerp/middleware.py +0 -0
  91. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/README.md +0 -0
  92. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/_auto_stamp.py +0 -0
  93. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/_data_reconcile.py +0 -0
  94. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/env.py +0 -0
  95. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/script.py.mako +0 -0
  96. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/a0b1c2d3e4f5_seed_landed_cost_clearing_accounts.py +0 -0
  97. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/a1b2c3d4e5f6_add_doc_share_tokens.py +0 -0
  98. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/b1c2d3e4f5a6_add_supporter_badges.py +0 -0
  99. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/b2c3d4e5f6a7_add_import_batches.py +0 -0
  100. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/c1d2e3f4a5b6_add_marketplace.py +0 -0
  101. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/d2e3f4a5b6c7_add_password_reset_token.py +0 -0
  102. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/e3f4a5b6c7d8_add_company_is_active.py +0 -0
  103. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/f1a2b3c4d5e6_move_accounts_marketplace_to_modules.py +0 -0
  104. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/fd5de461e14e_initial_schema.py +0 -0
  105. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/g1h2i3j4k5l6_add_consignment_flag.py +0 -0
  106. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/h2i3j4k5l6m7_add_bank_accounts_and_reconciliation.py +0 -0
  107. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/j4k5l6m7n8o9_labels_db_persistence.py +0 -0
  108. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/k5l6m7n8o9p0_add_reconciliation_v2.py +0 -0
  109. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/l6m7n8o9p0q1_add_sync_runs_table.py +0 -0
  110. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/m7n8o9p0q1r2_add_connector_configs_and_outbound_queue.py +0 -0
  111. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/p1q2r3s4t5u6_add_session_registry_and_user_auth_state.py +0 -0
  112. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/q2r3s4t5u6v7_add_system_runtime_state.py +0 -0
  113. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/r3s4t5u6v7w8_add_projections_company_entity_type_index.py +0 -0
  114. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/s4t5u6v7w8x9_drop_user_company_id_role.py +0 -0
  115. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/t5u6v7w8x9y0_seed_inventory_sub_accounts.py +0 -0
  116. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/x7y8z9a0b1c2_add_projection_created_at.py +0 -0
  117. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/y8z9a0b1c2d3_per_company_ledger_idempotency.py +0 -0
  118. {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/z9a0b1c2d3e4_work_centers.py +0 -0
  119. {celerp-1.2.6 → celerp-1.2.7}/celerp/models/__init__.py +0 -0
  120. {celerp-1.2.6 → celerp-1.2.7}/celerp/models/accounting.py +0 -0
  121. {celerp-1.2.6 → celerp-1.2.7}/celerp/models/ai.py +0 -0
  122. {celerp-1.2.6 → celerp-1.2.7}/celerp/models/auth.py +0 -0
  123. {celerp-1.2.6 → celerp-1.2.7}/celerp/models/base.py +0 -0
  124. {celerp-1.2.6 → celerp-1.2.7}/celerp/models/company.py +0 -0
  125. {celerp-1.2.6 → celerp-1.2.7}/celerp/models/connector_config.py +0 -0
  126. {celerp-1.2.6 → celerp-1.2.7}/celerp/models/import_batch.py +0 -0
  127. {celerp-1.2.6 → celerp-1.2.7}/celerp/models/ledger.py +0 -0
  128. {celerp-1.2.6 → celerp-1.2.7}/celerp/models/marketplace.py +0 -0
  129. {celerp-1.2.6 → celerp-1.2.7}/celerp/models/notification.py +0 -0
  130. {celerp-1.2.6 → celerp-1.2.7}/celerp/models/projections.py +0 -0
  131. {celerp-1.2.6 → celerp-1.2.7}/celerp/models/share.py +0 -0
  132. {celerp-1.2.6 → celerp-1.2.7}/celerp/models/supporter.py +0 -0
  133. {celerp-1.2.6 → celerp-1.2.7}/celerp/models/sync_run.py +0 -0
  134. {celerp-1.2.6 → celerp-1.2.7}/celerp/modules/__init__.py +0 -0
  135. {celerp-1.2.6 → celerp-1.2.7}/celerp/modules/api.py +0 -0
  136. {celerp-1.2.6 → celerp-1.2.7}/celerp/modules/audit.py +0 -0
  137. {celerp-1.2.6 → celerp-1.2.7}/celerp/modules/license.py +0 -0
  138. {celerp-1.2.6 → celerp-1.2.7}/celerp/modules/loader.py +0 -0
  139. {celerp-1.2.6 → celerp-1.2.7}/celerp/modules/references.py +0 -0
  140. {celerp-1.2.6 → celerp-1.2.7}/celerp/modules/registry.py +0 -0
  141. {celerp-1.2.6 → celerp-1.2.7}/celerp/modules/slots.py +0 -0
  142. {celerp-1.2.6 → celerp-1.2.7}/celerp/notifications/__init__.py +0 -0
  143. {celerp-1.2.6 → celerp-1.2.7}/celerp/notifications/service.py +0 -0
  144. {celerp-1.2.6 → celerp-1.2.7}/celerp/notifications/sse.py +0 -0
  145. {celerp-1.2.6 → celerp-1.2.7}/celerp/output/__init__.py +0 -0
  146. {celerp-1.2.6 → celerp-1.2.7}/celerp/output/pdf.py +0 -0
  147. {celerp-1.2.6 → celerp-1.2.7}/celerp/output/share_render.py +0 -0
  148. {celerp-1.2.6 → celerp-1.2.7}/celerp/projections/__init__.py +0 -0
  149. {celerp-1.2.6 → celerp-1.2.7}/celerp/projections/engine.py +0 -0
  150. {celerp-1.2.6 → celerp-1.2.7}/celerp/projections/handlers/__init__.py +0 -0
  151. {celerp-1.2.6 → celerp-1.2.7}/celerp/projections/handlers/marketplace.py +0 -0
  152. {celerp-1.2.6 → celerp-1.2.7}/celerp/projections/handlers/scanning.py +0 -0
  153. {celerp-1.2.6 → celerp-1.2.7}/celerp/projections/handlers/system.py +0 -0
  154. {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/__init__.py +0 -0
  155. {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/auth.py +0 -0
  156. {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/companies.py +0 -0
  157. {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/debug.py +0 -0
  158. {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/doctor.py +0 -0
  159. {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/events.py +0 -0
  160. {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/health.py +0 -0
  161. {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/ledger.py +0 -0
  162. {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/notifications.py +0 -0
  163. {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/stars.py +0 -0
  164. {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/system.py +0 -0
  165. {celerp-1.2.6 → celerp-1.2.7}/celerp/schemas/__init__.py +0 -0
  166. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/__init__.py +0 -0
  167. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/activity_redaction.py +0 -0
  168. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/attachments.py +0 -0
  169. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/auth.py +0 -0
  170. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/auto_je.py +0 -0
  171. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/backup.py +0 -0
  172. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/backup_export.py +0 -0
  173. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/backup_import.py +0 -0
  174. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/backup_repo.py +0 -0
  175. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/backup_scheduler.py +0 -0
  176. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/backup_state.py +0 -0
  177. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/demo.py +0 -0
  178. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/dev_release_guard.py +0 -0
  179. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/email.py +0 -0
  180. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/field_schema.py +0 -0
  181. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/fulfill.py +0 -0
  182. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/je_keys.py +0 -0
  183. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/landed_cost.py +0 -0
  184. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/line_measures.py +0 -0
  185. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/list_behavior.py +0 -0
  186. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/money.py +0 -0
  187. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/pick.py +0 -0
  188. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/runtime_state.py +0 -0
  189. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/session_tracker.py +0 -0
  190. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/star_cta.py +0 -0
  191. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/supporter_badge.py +0 -0
  192. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/system_health.py +0 -0
  193. {celerp-1.2.6 → celerp-1.2.7}/celerp/services/units.py +0 -0
  194. {celerp-1.2.6 → celerp-1.2.7}/celerp/session_gate.py +0 -0
  195. {celerp-1.2.6 → celerp-1.2.7}/celerp/tax_regimes.py +0 -0
  196. {celerp-1.2.6 → celerp-1.2.7}/celerp.egg-info/dependency_links.txt +0 -0
  197. {celerp-1.2.6 → celerp-1.2.7}/celerp.egg-info/entry_points.txt +0 -0
  198. {celerp-1.2.6 → celerp-1.2.7}/celerp.egg-info/requires.txt +0 -0
  199. {celerp-1.2.6 → celerp-1.2.7}/celerp.egg-info/top_level.txt +0 -0
  200. {celerp-1.2.6 → celerp-1.2.7}/conftest.py +0 -0
  201. {celerp-1.2.6 → celerp-1.2.7}/default_modules/__init__.py +0 -0
  202. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/LICENSE +0 -0
  203. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/__init__.py +0 -0
  204. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/celerp_accounting/__init__.py +0 -0
  205. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/celerp_accounting/api_setup.py +0 -0
  206. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/celerp_accounting/csv_parser.py +0 -0
  207. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/celerp_accounting/matcher.py +0 -0
  208. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/celerp_accounting/models.py +0 -0
  209. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/celerp_accounting/projections.py +0 -0
  210. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/celerp_accounting/routes.py +0 -0
  211. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/celerp_accounting/ui_routes.py +0 -0
  212. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/tests/test_accounting_banking.py +0 -0
  213. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/tests/test_accounting_banking_routers.py +0 -0
  214. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/tests/test_accounting_gaps.py +0 -0
  215. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/tests/test_accounting_import_idempotency.py +0 -0
  216. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/tests/test_reconciliation_v2.py +0 -0
  217. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-admin/__init__.py +0 -0
  218. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-admin/celerp_admin/__init__.py +0 -0
  219. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-admin/celerp_admin/routes.py +0 -0
  220. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-admin/celerp_admin/setup.py +0 -0
  221. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/__init__.py +0 -0
  222. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/celerp_ai/__init__.py +0 -0
  223. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/celerp_ai/routes.py +0 -0
  224. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/celerp_ai/setup.py +0 -0
  225. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/celerp_ai/ui_routes.py +0 -0
  226. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/conftest.py +0 -0
  227. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_batch.py +0 -0
  228. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_cleanup.py +0 -0
  229. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_conversations.py +0 -0
  230. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_coverage_gaps.py +0 -0
  231. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_intent.py +0 -0
  232. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_llm.py +0 -0
  233. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_memory.py +0 -0
  234. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_models.py +0 -0
  235. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_page_count.py +0 -0
  236. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_quota.py +0 -0
  237. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_router.py +0 -0
  238. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_routes_coverage.py +0 -0
  239. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_service.py +0 -0
  240. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_tools.py +0 -0
  241. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-backup/__init__.py +0 -0
  242. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-backup/celerp_backup/__init__.py +0 -0
  243. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-backup/celerp_backup/routes.py +0 -0
  244. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-backup/celerp_backup/setup.py +0 -0
  245. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/__init__.py +0 -0
  246. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/celerp_connectors/__init__.py +0 -0
  247. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/celerp_connectors/models.py +0 -0
  248. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/celerp_connectors/routes.py +0 -0
  249. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/__init__.py +0 -0
  250. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_images.py +0 -0
  251. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_rate_limiting.py +0 -0
  252. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_shopify.py +0 -0
  253. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_xero.py +0 -0
  254. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-contacts/LICENSE +0 -0
  255. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-contacts/__init__.py +0 -0
  256. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-contacts/celerp_contacts/__init__.py +0 -0
  257. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-contacts/celerp_contacts/migrations.py +0 -0
  258. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-contacts/celerp_contacts/projections.py +0 -0
  259. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-contacts/celerp_contacts/routes.py +0 -0
  260. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-contacts/celerp_contacts/ui_routes.py +0 -0
  261. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-dashboard/LICENSE +0 -0
  262. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-dashboard/__init__.py +0 -0
  263. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-dashboard/celerp_dashboard/__init__.py +0 -0
  264. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-dashboard/celerp_dashboard/routes.py +0 -0
  265. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-dashboard/celerp_dashboard/setup.py +0 -0
  266. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/LICENSE +0 -0
  267. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/__init__.py +0 -0
  268. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/__init__.py +0 -0
  269. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/api_setup.py +0 -0
  270. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/doc_constants.py +0 -0
  271. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/doc_projections.py +0 -0
  272. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/models_share.py +0 -0
  273. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/routes.py +0 -0
  274. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/routes_share.py +0 -0
  275. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/sequences.py +0 -0
  276. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/taxes.py +0 -0
  277. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/ui_routes.py +0 -0
  278. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/conftest.py +0 -0
  279. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_audits.py +0 -0
  280. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_freight_je.py +0 -0
  281. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_freight_lines.py +0 -0
  282. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_fulfill_no_split.py +0 -0
  283. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_fulfillment_activity.py +0 -0
  284. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_inbound_je_lifecycle.py +0 -0
  285. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_inventory_account_reconciliation.py +0 -0
  286. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_landed_receive.py +0 -0
  287. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_line_delete_persist.py +0 -0
  288. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_payment_void_activity.py +0 -0
  289. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_send_visibility.py +0 -0
  290. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_service_lines.py +0 -0
  291. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/LICENSE +0 -0
  292. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/__init__.py +0 -0
  293. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/celerp_inventory/__init__.py +0 -0
  294. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/celerp_inventory/models_import_batch.py +0 -0
  295. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/celerp_inventory/projections.py +0 -0
  296. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/celerp_inventory/routes.py +0 -0
  297. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/celerp_inventory/routes_attachments.py +0 -0
  298. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/celerp_inventory/routes_scanning.py +0 -0
  299. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/celerp_inventory/ui_routes.py +0 -0
  300. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/tests/test_item_recipe_projection.py +0 -0
  301. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/tests/test_item_workflow_projection.py +0 -0
  302. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/tests/test_landed_cost_projection.py +0 -0
  303. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/LICENSE +0 -0
  304. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/__init__.py +0 -0
  305. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/celerp_labels/__init__.py +0 -0
  306. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/celerp_labels/migrations/__init__.py +0 -0
  307. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/celerp_labels/migrations/labels_001_create_label_templates.py +0 -0
  308. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/celerp_labels/models.py +0 -0
  309. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/celerp_labels/presets.py +0 -0
  310. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/celerp_labels/routes.py +0 -0
  311. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/celerp_labels/service.py +0 -0
  312. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/celerp_labels/ui_routes.py +0 -0
  313. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/requirements.txt +0 -0
  314. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/tests/test_labels.py +0 -0
  315. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/tests/test_labels_module.py +0 -0
  316. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/LICENSE +0 -0
  317. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/__init__.py +0 -0
  318. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/celerp_manufacturing/__init__.py +0 -0
  319. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/celerp_manufacturing/costing.py +0 -0
  320. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/celerp_manufacturing/expansion.py +0 -0
  321. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/celerp_manufacturing/labor.py +0 -0
  322. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/celerp_manufacturing/migrations.py +0 -0
  323. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/celerp_manufacturing/projection_handler.py +0 -0
  324. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/celerp_manufacturing/routes.py +0 -0
  325. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/celerp_manufacturing/ui_routes.py +0 -0
  326. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/requirements.txt +0 -0
  327. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_bom_removed.py +0 -0
  328. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_build_api.py +0 -0
  329. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_item_recipe_api.py +0 -0
  330. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_item_workflow_api.py +0 -0
  331. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_labor_providers.py +0 -0
  332. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_manufacture_from_doc.py +0 -0
  333. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_manufacturing.py +0 -0
  334. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_manufacturing_import_idempotency.py +0 -0
  335. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_manufacturing_module.py +0 -0
  336. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_mfg_events.py +0 -0
  337. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_phase2_manufacturing_validations.py +0 -0
  338. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_production_order.py +0 -0
  339. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_recipe_costing.py +0 -0
  340. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_recipe_expansion.py +0 -0
  341. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_recost_api.py +0 -0
  342. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_to_make_allocation.py +0 -0
  343. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_where_used.py +0 -0
  344. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_work_centers_and_settings.py +0 -0
  345. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_workflow_time.py +0 -0
  346. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-reports/LICENSE +0 -0
  347. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-reports/__init__.py +0 -0
  348. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-reports/celerp_reports/__init__.py +0 -0
  349. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-reports/celerp_reports/api_setup.py +0 -0
  350. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-reports/celerp_reports/routes.py +0 -0
  351. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-reports/celerp_reports/ui_routes.py +0 -0
  352. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-reports/tests/test_reports_full.py +0 -0
  353. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-reports/tests/test_reports_unit.py +0 -0
  354. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-subscriptions/LICENSE +0 -0
  355. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-subscriptions/__init__.py +0 -0
  356. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-subscriptions/celerp_subscriptions/__init__.py +0 -0
  357. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-subscriptions/celerp_subscriptions/routes.py +0 -0
  358. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-subscriptions/celerp_subscriptions/ui_routes.py +0 -0
  359. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-subscriptions/tests/test_subscriptions.py +0 -0
  360. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-subscriptions/tests/test_subscriptions_ui.py +0 -0
  361. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/LICENSE +0 -0
  362. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/__init__.py +0 -0
  363. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/__init__.py +0 -0
  364. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/accessory_fashion.json +0 -0
  365. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/activewear.json +0 -0
  366. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/art_photography.json +0 -0
  367. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/audio_equipment.json +0 -0
  368. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/bag_handbag.json +0 -0
  369. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/banknote.json +0 -0
  370. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/beauty_tool.json +0 -0
  371. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/bed_bedroom.json +0 -0
  372. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/beer.json +0 -0
  373. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/beverage_nonalc.json +0 -0
  374. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/body_part_auto.json +0 -0
  375. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/book.json +0 -0
  376. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/bottoms.json +0 -0
  377. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/brake_suspension.json +0 -0
  378. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/bullion_coin.json +0 -0
  379. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/camera.json +0 -0
  380. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/colored_stone.json +0 -0
  381. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/comic.json +0 -0
  382. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/commercial_space.json +0 -0
  383. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/component_part.json +0 -0
  384. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/confectionery.json +0 -0
  385. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/consulting_service.json +0 -0
  386. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/decorative_art.json +0 -0
  387. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/diamond.json +0 -0
  388. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/drawing.json +0 -0
  389. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/dress_jumpsuit.json +0 -0
  390. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/electrical_auto.json +0 -0
  391. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/electrical_component.json +0 -0
  392. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/emerald.json +0 -0
  393. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/engine_part.json +0 -0
  394. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/fastener.json +0 -0
  395. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/fertilizer_chemical.json +0 -0
  396. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/film_video.json +0 -0
  397. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/fine_writing_instrument.json +0 -0
  398. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/fluid_lubricant.json +0 -0
  399. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/footwear.json +0 -0
  400. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/fragrance.json +0 -0
  401. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/fresh_food.json +0 -0
  402. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/fresh_produce.json +0 -0
  403. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/frozen_food.json +0 -0
  404. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/gaming_console.json +0 -0
  405. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/gold_bullion.json +0 -0
  406. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/grain_cereal.json +0 -0
  407. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/haircare.json +0 -0
  408. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/hand_tool.json +0 -0
  409. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/ingredient_bulk.json +0 -0
  410. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/interior_part.json +0 -0
  411. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/jewelry.json +0 -0
  412. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/kitchen_dining.json +0 -0
  413. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/laptop.json +0 -0
  414. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/lighting.json +0 -0
  415. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/livestock_feed.json +0 -0
  416. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/makeup.json +0 -0
  417. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/measuring_instrument.json +0 -0
  418. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/medal_token.json +0 -0
  419. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/mineral_specimen.json +0 -0
  420. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/mobile_phone.json +0 -0
  421. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/music_cd.json +0 -0
  422. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/music_vinyl.json +0 -0
  423. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/nail.json +0 -0
  424. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/numismatic_coin.json +0 -0
  425. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/outdoor_furniture.json +0 -0
  426. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/outerwear.json +0 -0
  427. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/packaged_food.json +0 -0
  428. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/painting.json +0 -0
  429. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/parking_bay.json +0 -0
  430. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/pearl.json +0 -0
  431. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/personal_care.json +0 -0
  432. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/pipe_plumbing.json +0 -0
  433. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/platinum_bullion.json +0 -0
  434. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/power_tool.json +0 -0
  435. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/print_edition.json +0 -0
  436. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/residential_unit.json +0 -0
  437. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/rough_gemstone.json +0 -0
  438. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/ruby.json +0 -0
  439. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/saas_plan.json +0 -0
  440. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/safety_ppe.json +0 -0
  441. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/sapphire.json +0 -0
  442. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/sculpture.json +0 -0
  443. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/seating.json +0 -0
  444. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/seeds.json +0 -0
  445. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/silver_bullion.json +0 -0
  446. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/skincare.json +0 -0
  447. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/soft_furnishing.json +0 -0
  448. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/software_addon.json +0 -0
  449. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/software_license.json +0 -0
  450. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/spirit.json +0 -0
  451. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/storage_furniture.json +0 -0
  452. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/swimwear.json +0 -0
  453. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/table_desk.json +0 -0
  454. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/tablet.json +0 -0
  455. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/tire_wheel.json +0 -0
  456. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/tops.json +0 -0
  457. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/trading_card.json +0 -0
  458. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/tv_display.json +0 -0
  459. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/video_game.json +0 -0
  460. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/watch.json +0 -0
  461. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/watch_strap.json +0 -0
  462. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/wine.json +0 -0
  463. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/agricultural.json +0 -0
  464. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/artwork.json +0 -0
  465. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/automotive.json +0 -0
  466. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/blank.json +0 -0
  467. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/books_media.json +0 -0
  468. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/coins_precious_metals.json +0 -0
  469. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/consulting.json +0 -0
  470. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/cosmetics.json +0 -0
  471. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/electronics.json +0 -0
  472. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/fashion.json +0 -0
  473. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/food_beverage.json +0 -0
  474. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/furniture.json +0 -0
  475. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/gemstones.json +0 -0
  476. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/hardware.json +0 -0
  477. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/property_rental.json +0 -0
  478. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/saas.json +0 -0
  479. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/watches.json +0 -0
  480. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/wine_spirits.json +0 -0
  481. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/routes.py +0 -0
  482. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/ui_routes.py +0 -0
  483. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/tests/test_verticals.py +0 -0
  484. {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/tests/test_verticals_routers.py +0 -0
  485. {celerp-1.2.6 → celerp-1.2.7}/legal/OFFICIAL-COMPONENTS.md +0 -0
  486. {celerp-1.2.6 → celerp-1.2.7}/legal/TRADEMARK.md +0 -0
  487. {celerp-1.2.6 → celerp-1.2.7}/memory/fix-receive-goods-bugs.md +0 -0
  488. {celerp-1.2.6 → celerp-1.2.7}/setup.cfg +0 -0
  489. {celerp-1.2.6 → celerp-1.2.7}/test_helpers.py +0 -0
  490. {celerp-1.2.6 → celerp-1.2.7}/ui/__init__.py +0 -0
  491. {celerp-1.2.6 → celerp-1.2.7}/ui/api_client.py +0 -0
  492. {celerp-1.2.6 → celerp-1.2.7}/ui/app.py +0 -0
  493. {celerp-1.2.6 → celerp-1.2.7}/ui/components/__init__.py +0 -0
  494. {celerp-1.2.6 → celerp-1.2.7}/ui/components/activity.py +0 -0
  495. {celerp-1.2.6 → celerp-1.2.7}/ui/components/backup.py +0 -0
  496. {celerp-1.2.6 → celerp-1.2.7}/ui/components/cloud_gate.py +0 -0
  497. {celerp-1.2.6 → celerp-1.2.7}/ui/components/currency.py +0 -0
  498. {celerp-1.2.6 → celerp-1.2.7}/ui/components/files.py +0 -0
  499. {celerp-1.2.6 → celerp-1.2.7}/ui/components/notes.py +0 -0
  500. {celerp-1.2.6 → celerp-1.2.7}/ui/components/phone.py +0 -0
  501. {celerp-1.2.6 → celerp-1.2.7}/ui/components/shell.py +0 -0
  502. {celerp-1.2.6 → celerp-1.2.7}/ui/components/table.py +0 -0
  503. {celerp-1.2.6 → celerp-1.2.7}/ui/config.py +0 -0
  504. {celerp-1.2.6 → celerp-1.2.7}/ui/i18n.py +0 -0
  505. {celerp-1.2.6 → celerp-1.2.7}/ui/locales/ar.json +0 -0
  506. {celerp-1.2.6 → celerp-1.2.7}/ui/locales/de.json +0 -0
  507. {celerp-1.2.6 → celerp-1.2.7}/ui/locales/en.json +0 -0
  508. {celerp-1.2.6 → celerp-1.2.7}/ui/locales/es.json +0 -0
  509. {celerp-1.2.6 → celerp-1.2.7}/ui/locales/fr.json +0 -0
  510. {celerp-1.2.6 → celerp-1.2.7}/ui/locales/id.json +0 -0
  511. {celerp-1.2.6 → celerp-1.2.7}/ui/locales/it.json +0 -0
  512. {celerp-1.2.6 → celerp-1.2.7}/ui/locales/ja.json +0 -0
  513. {celerp-1.2.6 → celerp-1.2.7}/ui/locales/pt.json +0 -0
  514. {celerp-1.2.6 → celerp-1.2.7}/ui/locales/th.json +0 -0
  515. {celerp-1.2.6 → celerp-1.2.7}/ui/locales/vi.json +0 -0
  516. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/__init__.py +0 -0
  517. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/accounting.py +0 -0
  518. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/accounting_import.py +0 -0
  519. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/auth.py +0 -0
  520. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/contacts.py +0 -0
  521. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/csv_import.py +0 -0
  522. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/dashboard.py +0 -0
  523. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/docs_import.py +0 -0
  524. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/documents.py +0 -0
  525. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/events.py +0 -0
  526. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/inventory.py +0 -0
  527. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/lists_import.py +0 -0
  528. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/manufacturing.py +0 -0
  529. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/notifications.py +0 -0
  530. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/reconciliation.py +0 -0
  531. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/reports.py +0 -0
  532. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/search.py +0 -0
  533. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings.py +0 -0
  534. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_accounting.py +0 -0
  535. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_cloud.py +0 -0
  536. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_contacts.py +0 -0
  537. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_general.py +0 -0
  538. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_import.py +0 -0
  539. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_inventory.py +0 -0
  540. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_manufacturing.py +0 -0
  541. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_purchasing.py +0 -0
  542. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_sales.py +0 -0
  543. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/setup.py +0 -0
  544. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/stars.py +0 -0
  545. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/subscriptions.py +0 -0
  546. {celerp-1.2.6 → celerp-1.2.7}/ui/routes/subscriptions_import.py +0 -0
  547. {celerp-1.2.6 → celerp-1.2.7}/ui/static/app.css +0 -0
  548. {celerp-1.2.6 → celerp-1.2.7}/ui/static/htmx.min.js +0 -0
  549. {celerp-1.2.6 → celerp-1.2.7}/ui/static/icon.png +0 -0
  550. {celerp-1.2.6 → celerp-1.2.7}/ui/static/logo.png +0 -0
  551. {celerp-1.2.6 → celerp-1.2.7}/ui/static/vendor/intl-tel-input/img/flags.webp +0 -0
  552. {celerp-1.2.6 → celerp-1.2.7}/ui/static/vendor/intl-tel-input/img/flags@2x.webp +0 -0
  553. {celerp-1.2.6 → celerp-1.2.7}/ui/static/vendor/intl-tel-input/intlTelInput.min.css +0 -0
  554. {celerp-1.2.6 → celerp-1.2.7}/ui/static/vendor/intl-tel-input/intlTelInput.min.js +0 -0
  555. {celerp-1.2.6 → celerp-1.2.7}/ui/static/vendor/intl-tel-input/intlTelInputWithUtils.min.js +0 -0
  556. {celerp-1.2.6 → celerp-1.2.7}/ui/static/vendor/intl-tel-input/utils.js +0 -0
@@ -17,11 +17,10 @@ follows:
17
17
  the recommended starting point: fork one, rename it (see `legal/TRADEMARK.md`), and license your derivative
18
18
  however you wish.
19
19
 
20
- **How CLA acceptance works.** Contributions to official Celerp repositories require CLA acceptance before
21
- merge. Opening a pull request does not by itself complete the CLA. When prompted by the CLA bot on your
22
- pull request, post the exact acceptance phrase from your authenticated GitHub account; that one-time
23
- acceptance is recorded against the current `CLA.md` version. The bot blocks merge until acceptance is
24
- recorded.
20
+ **How CLA acceptance works.** Contributions to official Celerp repositories require a one-time CLA
21
+ acceptance per user. If you haven't accepted yet, you'll be linked to a page that shows the agreement and
22
+ records your acceptance so that we know that you've read our rules and expectations. After that you can
23
+ contribute freely.
25
24
 
26
25
  Trademarks are not licensed by any code license; see `legal/TRADEMARK.md`.
27
26
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: celerp
3
- Version: 1.2.6
3
+ Version: 1.2.7
4
4
  Summary: Free, self-hosted ERP for small businesses
5
5
  License: Business Source License 1.1
6
6
 
@@ -206,20 +206,30 @@ def _post_migration_grants(db_url: str) -> None:
206
206
 
207
207
  Must run AFTER migrations since sequences/tables created by migrations
208
208
  won't be covered by ALTER DEFAULT PRIVILEGES set during provisioning.
209
+
210
+ Runs in-process over the existing connection rather than shelling out to
211
+ `sudo -u postgres psql` (which does not exist on Windows and crashed the
212
+ bundled launcher's migrate step). On the bundled single-user cluster the
213
+ connecting user is the superuser/owner, so the grants apply; best-effort so a
214
+ grant error never blocks startup.
209
215
  """
210
216
  parts = _parse_db_url(db_url)
211
217
  if not parts:
212
218
  return
213
219
  user = parts["user"]
214
- dbname = parts["dbname"]
215
- for grant_sql in [
216
- f"GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO {user};",
217
- f"GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO {user};",
218
- ]:
219
- subprocess.run(
220
- ["sudo", "-u", "postgres", "psql", "-d", dbname, "-c", grant_sql],
221
- capture_output=True, text=True,
222
- )
220
+ sync_url = db_url.replace("postgresql+asyncpg://", "postgresql://")
221
+ try:
222
+ from sqlalchemy import create_engine, text
223
+ engine = create_engine(sync_url)
224
+ try:
225
+ with engine.begin() as conn:
226
+ conn.execute(text(f'GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO "{user}";'))
227
+ conn.execute(text(f'GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO "{user}";'))
228
+ finally:
229
+ engine.dispose()
230
+ except Exception:
231
+ # Best-effort: on the bundled cluster the app user already owns its objects.
232
+ pass
223
233
 
224
234
 
225
235
 
@@ -24,6 +24,7 @@ from typing import Any
24
24
  import httpx
25
25
 
26
26
  from celerp.connectors.http import RateLimitedClient
27
+ from celerp.connectors.util import money
27
28
  from celerp.connectors.base import (
28
29
  ConnectorBase,
29
30
  ConnectorCategory,
@@ -37,7 +38,6 @@ import celerp.connectors.upsert as _upsert
37
38
  log = logging.getLogger(__name__)
38
39
 
39
40
  _API_BASE = "https://quickbooks.api.intuit.com/v3/company"
40
- _SANDBOX_BASE = "https://sandbox-quickbooks.api.intuit.com/v3/company"
41
41
  _PAGE_SIZE = 100
42
42
 
43
43
 
@@ -54,6 +54,10 @@ def _base_url(ctx: ConnectorContext) -> str:
54
54
  realm_id = ctx.store_handle or ""
55
55
  if not realm_id:
56
56
  raise ValueError("ConnectorContext.store_handle (realmId) is required for QuickBooks")
57
+ # realmId is an Intuit-issued numeric company id; reject anything else so it
58
+ # can never be used to redirect the request away from the Intuit API host.
59
+ if not realm_id.isdigit():
60
+ raise ValueError("QuickBooks realmId must be numeric")
57
61
  return f"{_API_BASE}/{realm_id}"
58
62
 
59
63
 
@@ -142,8 +146,6 @@ class QuickBooksConnector(ConnectorBase):
142
146
  continue
143
147
 
144
148
  idempotency_key = f"quickbooks:item:{qb_item['Id']}"
145
- sale_price = qb_item.get("UnitPrice")
146
- cost_price = qb_item.get("PurchaseCost")
147
149
  sell_by = "service" if item_type == "Service" else "piece"
148
150
 
149
151
  try:
@@ -153,8 +155,8 @@ class QuickBooksConnector(ConnectorBase):
153
155
  name=qb_item.get("Name") or sku,
154
156
  description=qb_item.get("Description") or "",
155
157
  sell_by=sell_by,
156
- sale_price=float(sale_price) if sale_price else None,
157
- cost_price=float(cost_price) if cost_price else None,
158
+ sale_price=money(qb_item.get("UnitPrice")),
159
+ cost_price=money(qb_item.get("PurchaseCost")),
158
160
  idempotency_key=idempotency_key,
159
161
  )
160
162
  created = await _upsert.upsert_item(ctx.company_id, item)
@@ -246,8 +248,8 @@ class QuickBooksConnector(ConnectorBase):
246
248
  result = SyncResult(entity=SyncEntity.INVOICES, direction=SyncDirection.OUTBOUND)
247
249
  errors: list[str] = []
248
250
  realm_id = (ctx.extra or {}).get("realm_id") or ctx.store_handle
249
- if not realm_id:
250
- result.errors = ["realm_id is required for QuickBooks outbound invoice sync"]
251
+ if not realm_id or not str(realm_id).isdigit():
252
+ result.errors = ["A numeric realm_id is required for QuickBooks outbound invoice sync"]
251
253
  return result
252
254
 
253
255
  try:
@@ -23,13 +23,13 @@ from typing import Any
23
23
  import httpx
24
24
 
25
25
  from celerp.connectors.http import RateLimitedClient
26
+ from celerp.connectors.util import money
26
27
  from celerp.connectors.base import (
27
28
  ConnectorBase,
28
29
  ConnectorCategory,
29
30
  ConnectorContext,
30
31
  SyncDirection,
31
32
  SyncEntity,
32
- SyncFrequency,
33
33
  SyncResult,
34
34
  )
35
35
  import celerp.connectors.upsert as _upsert
@@ -40,17 +40,6 @@ _API_VERSION = "2024-01"
40
40
  _PAGE_LIMIT = 250 # Shopify max per page
41
41
 
42
42
 
43
- def _money(v) -> float | None:
44
- """Parse a Shopify money value to float. Returns None only when genuinely
45
- absent (missing/null/empty) — a real price of 0 stays 0.0, not None."""
46
- if v is None or v == "":
47
- return None
48
- try:
49
- return float(v)
50
- except (TypeError, ValueError):
51
- return None
52
-
53
-
54
43
  def _base_url(ctx: ConnectorContext) -> str:
55
44
  if not ctx.store_handle:
56
45
  raise ValueError("ConnectorContext.store_handle is required for Shopify")
@@ -137,7 +126,7 @@ class ShopifyConnector(ConnectorBase):
137
126
  sku=sku,
138
127
  name=name,
139
128
  sell_by="piece",
140
- sale_price=_money(variant.get("price")),
129
+ sale_price=money(variant.get("price")),
141
130
  quantity=float(variant.get("inventory_quantity") or 0),
142
131
  idempotency_key=idempotency_key,
143
132
  )
@@ -173,17 +162,18 @@ class ShopifyConnector(ConnectorBase):
173
162
 
174
163
  images: list[dict] = product.get("images", [])
175
164
 
165
+ import uuid as _uuid
166
+
176
167
  from sqlalchemy import func
177
168
 
178
169
  async with get_async_session() as session:
179
- # Resolve the item by SKU with a DB-side filter (case-insensitive),
180
- # returning the single match — never load the whole catalog into
181
- # memory per product (that was O(products × catalog_size)).
170
+ # Resolve the item by SKU with a single DB-side, case-insensitive
171
+ # query that returns just the matching row.
182
172
  row = (await session.execute(
183
173
  select(Projection).where(
184
- Projection.company_id == ctx.company_id,
174
+ Projection.company_id == _uuid.UUID(str(ctx.company_id)),
185
175
  Projection.entity_type == "item",
186
- func.lower(Projection.state["sku"].astext) == sku.strip().lower(),
176
+ func.lower(Projection.state["sku"].as_string()) == sku.strip().lower(),
187
177
  ).limit(1)
188
178
  )).scalar_one_or_none()
189
179
  if row is None:
@@ -31,6 +31,32 @@ _SYNC_METHODS = {
31
31
  _OUTBOUND_ENTITIES = {"products_out", "invoices_out", "inventory_out"}
32
32
 
33
33
 
34
+ async def _last_success_watermark(company_id: str, connector: str, entity: str):
35
+ """The start time of the most recent FULLY successful sync for this
36
+ (company, connector, entity), used as the incremental `since`. Returns None on
37
+ the first sync or if it can't be read, which means a full pull.
38
+
39
+ Only fully-successful runs advance the cursor: a partial run left some records
40
+ errored, so the next run must re-pull from the last good point to retry them
41
+ rather than skipping past them."""
42
+ import sqlalchemy as sa
43
+
44
+ from celerp.db import get_session_ctx
45
+
46
+ try:
47
+ async with get_session_ctx() as session:
48
+ return await session.scalar(
49
+ sa.select(sa.func.max(SyncRun.started_at)).where(
50
+ SyncRun.company_id == company_id,
51
+ SyncRun.connector == connector,
52
+ SyncRun.entity == entity,
53
+ SyncRun.status == "success",
54
+ )
55
+ )
56
+ except Exception:
57
+ return None
58
+
59
+
34
60
  async def run_sync(
35
61
  connector: ConnectorBase,
36
62
  ctx: ConnectorContext,
@@ -66,6 +92,11 @@ async def run_sync(
66
92
  if sync_method is None:
67
93
  raise ValueError(f"{connector.name} has no method {method_name}")
68
94
 
95
+ # Incremental by default: pull only what changed since the last successful run
96
+ # for this entity. Idempotency keys make any overlap dup-safe.
97
+ if since is None and entity not in _OUTBOUND_ENTITIES:
98
+ since = await _last_success_watermark(ctx.company_id, connector.name, entity)
99
+
69
100
  started_at = datetime.now(timezone.utc)
70
101
 
71
102
  try:
@@ -1,6 +1,11 @@
1
1
  # Copyright (c) 2026 Noah Severs
2
2
  # SPDX-License-Identifier: BUSL-1.1
3
- """Shared upsert helpers for all connectors. Single source of truth."""
3
+ """Shared upsert helpers for all connectors. Single source of truth.
4
+
5
+ Each helper opens its own DB session (via the target service) so connectors
6
+ never manage session lifecycle. Imports are deferred to keep connector import
7
+ light and to respect default-module load order.
8
+ """
4
9
  from __future__ import annotations
5
10
 
6
11
 
@@ -10,23 +15,28 @@ async def upsert_item(company_id: str, item) -> bool:
10
15
 
11
16
 
12
17
  async def upsert_order_from_shopify(company_id: str, order: dict) -> bool:
13
- from celerp.services import docs as docs_svc
18
+ from celerp_docs import doc_service as docs_svc
14
19
  return await docs_svc.upsert_order_from_shopify(company_id, order)
15
20
 
16
21
 
17
22
  async def upsert_invoice_from_quickbooks(company_id: str, invoice: dict) -> bool:
18
- from celerp.services import docs as docs_svc
23
+ from celerp_docs import doc_service as docs_svc
19
24
  return await docs_svc.upsert_invoice_from_quickbooks(company_id, invoice)
20
25
 
21
26
 
22
27
  async def upsert_invoice_from_xero(company_id: str, invoice: dict) -> bool:
23
- from celerp.services import docs as docs_svc
28
+ from celerp_docs import doc_service as docs_svc
24
29
  return await docs_svc.upsert_invoice_from_xero(company_id, invoice)
25
30
 
26
31
 
32
+ async def upsert_order_from_woocommerce(company_id: str, order: dict) -> bool:
33
+ from celerp_docs import doc_service as docs_svc
34
+ return await docs_svc.upsert_order_from_woocommerce(company_id, order)
35
+
36
+
27
37
  async def upsert_contact_from_shopify(company_id: str, customer: dict) -> bool:
28
- from celerp_sales_funnel import services as crm_svc
29
- return await crm_svc.upsert_contact_from_shopify(company_id, customer)
38
+ from celerp_contacts import services as contacts_svc
39
+ return await contacts_svc.upsert_contact_from_shopify(company_id, customer)
30
40
 
31
41
 
32
42
  async def upsert_contact_from_quickbooks(company_id: str, customer: dict) -> bool:
@@ -39,6 +49,11 @@ async def upsert_contact_from_xero(company_id: str, contact: dict) -> bool:
39
49
  return await contacts_svc.upsert_contact_from_xero(company_id, contact)
40
50
 
41
51
 
52
+ async def upsert_contact_from_woocommerce(company_id: str, customer: dict) -> bool:
53
+ from celerp_contacts import services as contacts_svc
54
+ return await contacts_svc.upsert_contact_from_woocommerce(company_id, customer)
55
+
56
+
42
57
  async def list_items_with_external_id(company_id: str, platform: str) -> list:
43
58
  from celerp_inventory import services as items_svc
44
59
  return await items_svc.list_items_with_external_id(company_id, platform=platform)
@@ -49,16 +64,6 @@ async def list_items_modified_since_last_sync(company_id: str, platform: str) ->
49
64
  return await items_svc.list_items_modified_since_last_sync(company_id, platform=platform)
50
65
 
51
66
 
52
- async def upsert_order_from_woocommerce(company_id: str, order: dict) -> bool:
53
- from celerp.services import docs as docs_svc
54
- return await docs_svc.upsert_order_from_woocommerce(company_id, order)
55
-
56
-
57
- async def upsert_contact_from_woocommerce(company_id: str, customer: dict) -> bool:
58
- from celerp_contacts import services as contacts_svc
59
- return await contacts_svc.upsert_contact_from_woocommerce(company_id, customer)
60
-
61
-
62
67
  async def list_unsynced_invoices(company_id: str, platform: str) -> list:
63
- from celerp.services import docs as docs_svc
68
+ from celerp_docs import doc_service as docs_svc
64
69
  return await docs_svc.list_unsynced_invoices(company_id, platform=platform)
@@ -0,0 +1,19 @@
1
+ # Copyright (c) 2026 Noah Severs
2
+ # SPDX-License-Identifier: BUSL-1.1
3
+ """Shared helpers for platform connectors."""
4
+ from __future__ import annotations
5
+
6
+
7
+ def money(v) -> float | None:
8
+ """Parse a platform money value to float.
9
+
10
+ Returns None only when the value is genuinely absent (missing/null/empty) or
11
+ unparseable. A real price of 0 stays 0.0 - never collapsed to None (a free or
12
+ zero-priced item is a valid price, not a missing one).
13
+ """
14
+ if v is None or v == "":
15
+ return None
16
+ try:
17
+ return float(v)
18
+ except (TypeError, ValueError):
19
+ return None
@@ -74,3 +74,49 @@ async def handle_webhook(
74
74
  # Pass since=None so the sync methods use the last SyncRun timestamp.
75
75
  await run_sync(connector, ctx, entity, direction=direction)
76
76
  log.info("webhook: processed %s/%s for %s", event.platform, event.topic, ctx.company_id)
77
+
78
+
79
+ async def dispatch_woocommerce_webhook(raw_body: bytes, signature: str, topic: str) -> bool:
80
+ """Verify and process a WooCommerce webhook the relay forwarded to this instance.
81
+
82
+ WooCommerce signs the raw body with the per-config secret (base64 HMAC-SHA256,
83
+ sent as X-WC-Webhook-Signature). We try each configured WooCommerce company's
84
+ secret; the first that verifies owns the event. Returns True if handled, False
85
+ if no configured secret validated the signature (a forged delivery is ignored).
86
+ """
87
+ import json
88
+
89
+ import sqlalchemy as sa
90
+
91
+ from celerp.connectors.relay_token import fetch_context
92
+ from celerp.db import get_session_ctx
93
+ from celerp.models.connector_config import ConnectorConfig
94
+
95
+ if not signature:
96
+ return False
97
+
98
+ connector = connector_registry.get("woocommerce")
99
+
100
+ async with get_session_ctx() as session:
101
+ rows = await session.execute(
102
+ sa.select(
103
+ ConnectorConfig.company_id, ConnectorConfig.direction, ConnectorConfig.webhook_secret
104
+ ).where(ConnectorConfig.connector == "woocommerce")
105
+ )
106
+ configs = rows.all()
107
+
108
+ for company_id, direction, secret in configs:
109
+ if not secret or not connector.validate_webhook(raw_body, signature, secret):
110
+ continue
111
+ ctx = await fetch_context(company_id, "woocommerce")
112
+ if ctx is None:
113
+ continue
114
+ try:
115
+ data = json.loads(raw_body or b"{}")
116
+ except (ValueError, TypeError):
117
+ data = {}
118
+ event = WebhookEvent(platform="woocommerce", topic=topic, payload=data)
119
+ await handle_webhook(event, ctx, direction=SyncDirection(direction))
120
+ return True
121
+
122
+ return False
@@ -20,13 +20,13 @@ import httpx
20
20
  import json
21
21
 
22
22
  from celerp.connectors.http import RateLimitedClient
23
+ from celerp.connectors.util import money
23
24
  from celerp.connectors.base import (
24
25
  ConnectorBase,
25
26
  ConnectorCategory,
26
27
  ConnectorContext,
27
28
  SyncDirection,
28
29
  SyncEntity,
29
- SyncFrequency,
30
30
  SyncResult,
31
31
  )
32
32
  import celerp.connectors.upsert as _upsert
@@ -61,6 +61,7 @@ class WooCommerceConnector(ConnectorBase):
61
61
  "products": "external_wins",
62
62
  "orders": "external_wins",
63
63
  "contacts": "external_wins",
64
+ "inventory": "internal_wins", # stock is pushed out; CelERP is the source
64
65
  }
65
66
 
66
67
  # -- Internal helpers ------------------------------------------------------
@@ -88,8 +89,15 @@ class WooCommerceConnector(ConnectorBase):
88
89
  resp.raise_for_status()
89
90
  data = resp.json()
90
91
  results.extend(data)
91
- total_pages = int(resp.headers.get("X-WP-TotalPages", 1))
92
- if page >= total_pages:
92
+ if not data:
93
+ break
94
+ total_pages_hdr = resp.headers.get("X-WP-TotalPages")
95
+ if total_pages_hdr is not None:
96
+ if page >= int(total_pages_hdr):
97
+ break
98
+ elif len(data) < _PER_PAGE:
99
+ # No total-pages header (proxy stripped it / error envelope):
100
+ # keep paging until a short page rather than truncating at 1.
93
101
  break
94
102
  page += 1
95
103
 
@@ -127,11 +135,11 @@ class WooCommerceConnector(ConnectorBase):
127
135
  pid = product.get("id")
128
136
  sku = (product.get("sku") or "").strip() or f"WC-{pid}"
129
137
  name = product.get("name") or sku
130
- sell_price_raw = product.get("regular_price") or product.get("price") or "0"
131
- try:
132
- sell_price = float(sell_price_raw) or None
133
- except (ValueError, TypeError):
134
- sell_price = None
138
+ # regular_price is the base price; fall back to the active price only
139
+ # when regular_price is genuinely absent. A real 0 (free item) is kept.
140
+ sell_price = money(product.get("regular_price"))
141
+ if sell_price is None:
142
+ sell_price = money(product.get("price"))
135
143
 
136
144
  idempotency_key = f"woocommerce:{pid}"
137
145
 
@@ -167,22 +175,25 @@ class WooCommerceConnector(ConnectorBase):
167
175
 
168
176
  async def _pull_product_files(self, ctx: ConnectorContext, product: dict[str, Any], sku: str) -> None:
169
177
  """Pull images and cert metafields from a WC product and store as item files."""
178
+ import uuid as _uuid
170
179
  from celerp.db import get_session_ctx as get_async_session
171
180
  from celerp.models.projections import Projection
172
181
  from celerp.connectors.images import download_and_emit_file, _CERT_TAGS
173
- from sqlalchemy import select
182
+ from sqlalchemy import func, select
174
183
 
175
184
  images: list[dict] = product.get("images", [])
176
185
  meta_data: list[dict] = product.get("meta_data", [])
177
186
 
178
187
  async with get_async_session() as session:
179
- rows = (await session.execute(
188
+ # Resolve the item by SKU with a single DB-side, case-insensitive
189
+ # query, limited to the one matching row.
190
+ row = (await session.execute(
180
191
  select(Projection).where(
181
- Projection.company_id == ctx.company_id,
192
+ Projection.company_id == _uuid.UUID(str(ctx.company_id)),
182
193
  Projection.entity_type == "item",
183
- )
184
- )).scalars().all()
185
- row = next((r for r in rows if str(r.state.get("sku", "")).strip().lower() == sku.strip().lower()), None)
194
+ func.lower(Projection.state["sku"].as_string()) == sku.strip().lower(),
195
+ ).limit(1)
196
+ )).scalar_one_or_none()
186
197
  if row is None:
187
198
  return
188
199
 
@@ -399,28 +410,32 @@ class WooCommerceConnector(ConnectorBase):
399
410
  return []
400
411
  return self._INBOUND_TOPICS
401
412
 
402
- async def register_webhooks(self, ctx: ConnectorContext, webhook_url: str) -> list[str]:
403
- """Register WooCommerce webhooks via REST API."""
413
+ async def register_webhooks(
414
+ self, ctx: ConnectorContext, webhook_url: str, secret: str | None = None
415
+ ) -> list[str]:
416
+ """Register WooCommerce webhooks via REST API.
417
+
418
+ When `secret` is supplied it is set on every webhook so deliveries are
419
+ signed with a value we already hold (X-WC-Webhook-Signature = base64
420
+ HMAC-SHA256 of the body), letting the receiver verify them. Returns the
421
+ created webhook ids."""
404
422
  base_url = _base_url(ctx)
405
423
  auth = _auth(ctx)
406
424
  ids: list[str] = []
407
425
  topics = self.webhook_topics_for_direction(self.direction)
408
426
  async with RateLimitedClient() as client:
409
427
  for topic in topics:
410
- resp = await client.post(
411
- f"{base_url}/webhooks",
412
- auth=auth,
413
- json={
414
- "name": f"CelERP {topic}",
415
- "topic": topic,
416
- "delivery_url": webhook_url,
417
- "status": "active",
418
- },
419
- )
428
+ body = {
429
+ "name": f"CelERP {topic}",
430
+ "topic": topic,
431
+ "delivery_url": webhook_url,
432
+ "status": "active",
433
+ }
434
+ if secret:
435
+ body["secret"] = secret
436
+ resp = await client.post(f"{base_url}/webhooks", auth=auth, json=body)
420
437
  if resp.status_code in (200, 201):
421
- wh = resp.json()
422
- ids.append(str(wh.get("id", "")))
423
- # WC generates its own secret, stored in the response
438
+ ids.append(str(resp.json().get("id", "")))
424
439
  else:
425
440
  log.warning("woocommerce.register_webhook topic=%s status=%d", topic, resp.status_code)
426
441
  return ids
@@ -23,6 +23,7 @@ from typing import Any
23
23
  import httpx
24
24
 
25
25
  from celerp.connectors.http import RateLimitedClient
26
+ from celerp.connectors.util import money
26
27
  from celerp.connectors.base import (
27
28
  ConnectorBase,
28
29
  ConnectorCategory,
@@ -64,8 +65,17 @@ class XeroConnector(ConnectorBase):
64
65
 
65
66
  # -- Internal helpers ------------------------------------------------------
66
67
 
67
- async def _paginate(self, ctx: ConnectorContext, path: str, key: str, since: datetime | None = None) -> list[dict[str, Any]]:
68
- """Fetch all pages for a resource using Xero's page-based pagination."""
68
+ async def _paginate(
69
+ self,
70
+ ctx: ConnectorContext,
71
+ path: str,
72
+ key: str,
73
+ since: datetime | None = None,
74
+ paginated: bool = True,
75
+ ) -> list[dict[str, Any]]:
76
+ """Fetch a resource. Paginating endpoints (Invoices, Contacts) use Xero's
77
+ page-based paging; endpoints that return the full set in one response
78
+ (Items) pass paginated=False and are fetched in a single request."""
69
79
  results: list[dict[str, Any]] = []
70
80
  page = 1
71
81
  headers = _headers(ctx)
@@ -73,16 +83,13 @@ class XeroConnector(ConnectorBase):
73
83
  headers["If-Modified-Since"] = since.strftime("%a, %d %b %Y %H:%M:%S GMT")
74
84
  async with RateLimitedClient() as client:
75
85
  while True:
76
- resp = await client.get(
77
- f"{_API_BASE}{path}",
78
- headers=headers,
79
- params={"page": page, "pageSize": _PAGE_SIZE},
80
- )
86
+ params = {"page": page, "pageSize": _PAGE_SIZE} if paginated else None
87
+ resp = await client.get(f"{_API_BASE}{path}", headers=headers, params=params)
81
88
  resp.raise_for_status()
82
89
  data = resp.json()
83
90
  items = data.get(key, [])
84
91
  results.extend(items)
85
- if len(items) < _PAGE_SIZE:
92
+ if not paginated or len(items) < _PAGE_SIZE:
86
93
  break
87
94
  page += 1
88
95
  return results
@@ -105,7 +112,7 @@ class XeroConnector(ConnectorBase):
105
112
  errors: list[str] = []
106
113
 
107
114
  try:
108
- items = await self._paginate(ctx, "/Items", "Items", since=since)
115
+ items = await self._paginate(ctx, "/Items", "Items", since=since, paginated=False)
109
116
  except httpx.HTTPStatusError as exc:
110
117
  result.errors = [f"Xero API error: {exc}"]
111
118
  return result
@@ -117,8 +124,6 @@ class XeroConnector(ConnectorBase):
117
124
  continue
118
125
 
119
126
  idempotency_key = f"xero:item:{xero_item['ItemID']}"
120
- sale_price = (xero_item.get("SalesDetails") or {}).get("UnitPrice")
121
- cost_price = (xero_item.get("PurchaseDetails") or {}).get("UnitPrice")
122
127
 
123
128
  try:
124
129
  from celerp_inventory.routes import ItemCreate
@@ -127,8 +132,8 @@ class XeroConnector(ConnectorBase):
127
132
  name=xero_item.get("Name") or sku,
128
133
  description=xero_item.get("Description") or "",
129
134
  sell_by="piece",
130
- sale_price=float(sale_price) if sale_price else None,
131
- cost_price=float(cost_price) if cost_price else None,
135
+ sale_price=money((xero_item.get("SalesDetails") or {}).get("UnitPrice")),
136
+ cost_price=money((xero_item.get("PurchaseDetails") or {}).get("UnitPrice")),
132
137
  idempotency_key=idempotency_key,
133
138
  )
134
139
  created = await _upsert.upsert_item(ctx.company_id, item)
@@ -243,6 +243,9 @@ class GatewayClient:
243
243
  elif msg_type == "shopify.webhook":
244
244
  asyncio.create_task(self._handle_shopify_webhook(payload))
245
245
 
246
+ elif msg_type == "woocommerce.webhook":
247
+ asyncio.create_task(self._handle_woocommerce_webhook(payload))
248
+
246
249
  else:
247
250
  log.debug("Unhandled gateway message type: %s", msg_type)
248
251
 
@@ -302,6 +305,22 @@ class GatewayClient:
302
305
  except Exception as exc:
303
306
  log.warning("shopify webhook handling failed (topic=%s): %s", topic, exc)
304
307
 
308
+ async def _handle_woocommerce_webhook(self, payload: dict) -> None:
309
+ """A WooCommerce webhook the relay forwarded. The per-install signing
310
+ secret lives here, so the signature is verified locally before a targeted
311
+ sync runs (a forged delivery verifies against no secret and is ignored)."""
312
+ topic = payload.get("topic", "")
313
+ try:
314
+ import base64
315
+
316
+ from celerp.connectors.webhooks import dispatch_woocommerce_webhook
317
+
318
+ raw = base64.b64decode(payload.get("body_b64") or "")
319
+ signature = payload.get("signature", "")
320
+ await dispatch_woocommerce_webhook(raw, signature, topic)
321
+ except Exception as exc:
322
+ log.warning("woocommerce webhook handling failed (topic=%s): %s", topic, exc)
323
+
305
324
  async def _handle_proxy_request(self, payload: dict) -> None:
306
325
  """Handle a proxied HTTP request from the relay.
307
326