celerp 1.2.7__tar.gz → 1.2.8__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.7 → celerp-1.2.8}/PKG-INFO +1 -1
  2. {celerp-1.2.7 → celerp-1.2.8}/celerp/connectors/base.py +17 -7
  3. {celerp-1.2.7 → celerp-1.2.8}/celerp/connectors/daily_scheduler.py +38 -9
  4. {celerp-1.2.7 → celerp-1.2.8}/celerp/connectors/http.py +17 -1
  5. {celerp-1.2.7 → celerp-1.2.8}/celerp/connectors/quickbooks.py +13 -21
  6. {celerp-1.2.7 → celerp-1.2.8}/celerp/connectors/shopify.py +14 -62
  7. {celerp-1.2.7 → celerp-1.2.8}/celerp/connectors/sync_runner.py +97 -26
  8. {celerp-1.2.7 → celerp-1.2.8}/celerp/connectors/upsert.py +16 -9
  9. {celerp-1.2.7 → celerp-1.2.8}/celerp/connectors/webhooks.py +9 -15
  10. {celerp-1.2.7 → celerp-1.2.8}/celerp/connectors/woocommerce.py +123 -116
  11. {celerp-1.2.7 → celerp-1.2.8}/celerp/connectors/xero.py +13 -21
  12. {celerp-1.2.7 → celerp-1.2.8}/celerp/events/engine.py +65 -0
  13. {celerp-1.2.7 → celerp-1.2.8}/celerp/events/schemas.py +17 -0
  14. {celerp-1.2.7 → celerp-1.2.8}/celerp/gateway/client.py +42 -9
  15. celerp-1.2.8/celerp/migrations/versions/c2d3e4f5a6b7_add_is_sync_to_shopify.py +35 -0
  16. celerp-1.2.8/celerp/migrations/versions/e4f5a6b7c8d9_backfill_connector_idempotency_key.py +98 -0
  17. {celerp-1.2.7 → celerp-1.2.8}/celerp/models/__init__.py +1 -0
  18. {celerp-1.2.7 → celerp-1.2.8}/celerp/models/projections.py +1 -0
  19. {celerp-1.2.7 → celerp-1.2.8}/celerp/models/sync_run.py +1 -1
  20. {celerp-1.2.7 → celerp-1.2.8}/celerp/projections/engine.py +2 -0
  21. celerp-1.2.8/celerp/projections/handlers/shopify.py +23 -0
  22. {celerp-1.2.7 → celerp-1.2.8}/celerp/routers/system.py +1 -1
  23. {celerp-1.2.7 → celerp-1.2.8}/celerp.egg-info/PKG-INFO +1 -1
  24. {celerp-1.2.7 → celerp-1.2.8}/celerp.egg-info/SOURCES.txt +3 -1
  25. {celerp-1.2.7 → celerp-1.2.8}/celerp.egg-info/scm_file_list.json +14 -1
  26. celerp-1.2.8/celerp.egg-info/scm_version.json +8 -0
  27. {celerp-1.2.7 → celerp-1.2.8}/conftest.py +8 -0
  28. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-connectors/__init__.py +27 -4
  29. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-connectors/celerp_connectors/routes.py +5 -12
  30. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-connectors/tests/conftest.py +7 -7
  31. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-connectors/tests/test_compliance.py +1 -5
  32. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-connectors/tests/test_connector_correctness.py +5 -98
  33. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-connectors/tests/test_connectors.py +15 -262
  34. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-connectors/tests/test_outbound.py +3 -3
  35. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-connectors/tests/test_shopify.py +1 -1
  36. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-connectors/tests/test_wiring.py +25 -3
  37. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-connectors/tests/test_woocommerce.py +25 -98
  38. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-connectors/tests/test_woocommerce_webhooks.py +2 -2
  39. celerp-1.2.8/default_modules/celerp-contacts/celerp_contacts/services.py +126 -0
  40. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/celerp_docs/doc_projections.py +7 -0
  41. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/celerp_docs/doc_service.py +92 -89
  42. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-inventory/celerp_inventory/projections.py +20 -2
  43. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-inventory/celerp_inventory/routes.py +112 -12
  44. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-inventory/celerp_inventory/services.py +47 -54
  45. {celerp-1.2.7 → celerp-1.2.8}/ui/api_client.py +5 -0
  46. {celerp-1.2.7 → celerp-1.2.8}/ui/components/table.py +5 -1
  47. {celerp-1.2.7 → celerp-1.2.8}/ui/locales/en.json +23 -4
  48. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/auth.py +9 -0
  49. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/inventory.py +79 -33
  50. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/settings_connectors.py +404 -112
  51. {celerp-1.2.7 → celerp-1.2.8}/ui/static/app.css +1 -0
  52. celerp-1.2.7/celerp.egg-info/scm_version.json +0 -8
  53. celerp-1.2.7/default_modules/celerp-contacts/celerp_contacts/services.py +0 -181
  54. celerp-1.2.7/memory/fix-receive-goods-bugs.md +0 -1
  55. {celerp-1.2.7 → celerp-1.2.8}/CLA.md +0 -0
  56. {celerp-1.2.7 → celerp-1.2.8}/CONTRIBUTING.md +0 -0
  57. {celerp-1.2.7 → celerp-1.2.8}/LICENSE +0 -0
  58. {celerp-1.2.7 → celerp-1.2.8}/LICENSING.md +0 -0
  59. {celerp-1.2.7 → celerp-1.2.8}/MANIFEST.in +0 -0
  60. {celerp-1.2.7 → celerp-1.2.8}/NOTICE +0 -0
  61. {celerp-1.2.7 → celerp-1.2.8}/README.md +0 -0
  62. {celerp-1.2.7 → celerp-1.2.8}/alembic.ini +0 -0
  63. {celerp-1.2.7 → celerp-1.2.8}/celerp/__init__.py +0 -0
  64. {celerp-1.2.7 → celerp-1.2.8}/celerp/__main__.py +0 -0
  65. {celerp-1.2.7 → celerp-1.2.8}/celerp/ai/__init__.py +0 -0
  66. {celerp-1.2.7 → celerp-1.2.8}/celerp/ai/batch.py +0 -0
  67. {celerp-1.2.7 → celerp-1.2.8}/celerp/ai/cleanup.py +0 -0
  68. {celerp-1.2.7 → celerp-1.2.8}/celerp/ai/commands.py +0 -0
  69. {celerp-1.2.7 → celerp-1.2.8}/celerp/ai/conversations.py +0 -0
  70. {celerp-1.2.7 → celerp-1.2.8}/celerp/ai/files.py +0 -0
  71. {celerp-1.2.7 → celerp-1.2.8}/celerp/ai/intent.py +0 -0
  72. {celerp-1.2.7 → celerp-1.2.8}/celerp/ai/llm.py +0 -0
  73. {celerp-1.2.7 → celerp-1.2.8}/celerp/ai/memory.py +0 -0
  74. {celerp-1.2.7 → celerp-1.2.8}/celerp/ai/models.py +0 -0
  75. {celerp-1.2.7 → celerp-1.2.8}/celerp/ai/page_count.py +0 -0
  76. {celerp-1.2.7 → celerp-1.2.8}/celerp/ai/quota.py +0 -0
  77. {celerp-1.2.7 → celerp-1.2.8}/celerp/ai/service.py +0 -0
  78. {celerp-1.2.7 → celerp-1.2.8}/celerp/ai/tools.py +0 -0
  79. {celerp-1.2.7 → celerp-1.2.8}/celerp/alembic.ini +0 -0
  80. {celerp-1.2.7 → celerp-1.2.8}/celerp/alembic_config.py +0 -0
  81. {celerp-1.2.7 → celerp-1.2.8}/celerp/cli.py +0 -0
  82. {celerp-1.2.7 → celerp-1.2.8}/celerp/compute/__init__.py +0 -0
  83. {celerp-1.2.7 → celerp-1.2.8}/celerp/compute/aggregations.py +0 -0
  84. {celerp-1.2.7 → celerp-1.2.8}/celerp/compute/valuation.py +0 -0
  85. {celerp-1.2.7 → celerp-1.2.8}/celerp/config.py +0 -0
  86. {celerp-1.2.7 → celerp-1.2.8}/celerp/connectors/__init__.py +0 -0
  87. {celerp-1.2.7 → celerp-1.2.8}/celerp/connectors/images.py +0 -0
  88. {celerp-1.2.7 → celerp-1.2.8}/celerp/connectors/registry.py +0 -0
  89. {celerp-1.2.7 → celerp-1.2.8}/celerp/connectors/relay_token.py +0 -0
  90. {celerp-1.2.7 → celerp-1.2.8}/celerp/connectors/util.py +0 -0
  91. {celerp-1.2.7 → celerp-1.2.8}/celerp/constants.py +0 -0
  92. {celerp-1.2.7 → celerp-1.2.8}/celerp/db.py +0 -0
  93. {celerp-1.2.7 → celerp-1.2.8}/celerp/events/__init__.py +0 -0
  94. {celerp-1.2.7 → celerp-1.2.8}/celerp/events/types.py +0 -0
  95. {celerp-1.2.7 → celerp-1.2.8}/celerp/gateway/__init__.py +0 -0
  96. {celerp-1.2.7 → celerp-1.2.8}/celerp/gateway/state.py +0 -0
  97. {celerp-1.2.7 → celerp-1.2.8}/celerp/importers/__init__.py +0 -0
  98. {celerp-1.2.7 → celerp-1.2.8}/celerp/importers/importer.py +0 -0
  99. {celerp-1.2.7 → celerp-1.2.8}/celerp/importers/schema.py +0 -0
  100. {celerp-1.2.7 → celerp-1.2.8}/celerp/main.py +0 -0
  101. {celerp-1.2.7 → celerp-1.2.8}/celerp/middleware.py +0 -0
  102. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/README.md +0 -0
  103. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/_auto_stamp.py +0 -0
  104. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/_data_reconcile.py +0 -0
  105. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/_json_compat.py +0 -0
  106. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/env.py +0 -0
  107. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/script.py.mako +0 -0
  108. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/a0b1c2d3e4f5_seed_landed_cost_clearing_accounts.py +0 -0
  109. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/a1b2c3d4e5f6_add_doc_share_tokens.py +0 -0
  110. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/b1c2d3e4f5a6_add_supporter_badges.py +0 -0
  111. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/b2c3d4e5f6a7_add_import_batches.py +0 -0
  112. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/c1d2e3f4a5b6_add_marketplace.py +0 -0
  113. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/d2e3f4a5b6c7_add_password_reset_token.py +0 -0
  114. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/e3f4a5b6c7d8_add_company_is_active.py +0 -0
  115. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/f1a2b3c4d5e6_move_accounts_marketplace_to_modules.py +0 -0
  116. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/fd5de461e14e_initial_schema.py +0 -0
  117. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/g1h2i3j4k5l6_add_consignment_flag.py +0 -0
  118. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/h2i3j4k5l6m7_add_bank_accounts_and_reconciliation.py +0 -0
  119. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/j4k5l6m7n8o9_labels_db_persistence.py +0 -0
  120. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/k5l6m7n8o9p0_add_reconciliation_v2.py +0 -0
  121. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/l6m7n8o9p0q1_add_sync_runs_table.py +0 -0
  122. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/m7n8o9p0q1r2_add_connector_configs_and_outbound_queue.py +0 -0
  123. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/n8o9p0q1r2s3_backfill_payment_date.py +0 -0
  124. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/o9p0q1r2s3t4_backfill_item_price_fields.py +0 -0
  125. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/p1q2r3s4t5u6_add_session_registry_and_user_auth_state.py +0 -0
  126. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/q2r3s4t5u6v7_add_system_runtime_state.py +0 -0
  127. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/r3s4t5u6v7w8_add_projections_company_entity_type_index.py +0 -0
  128. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/s4t5u6v7w8x9_drop_user_company_id_role.py +0 -0
  129. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/t5u6v7w8x9y0_seed_inventory_sub_accounts.py +0 -0
  130. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/u6v7w8x9y0z1_backfill_item_prices_from_attributes.py +0 -0
  131. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/v7w8x9y0z1a2_backfill_purchase_unit_defaults.py +0 -0
  132. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/w8x9y0z1a2b3_backfill_category_unit_defaults.py +0 -0
  133. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/x7y8z9a0b1c2_add_projection_created_at.py +0 -0
  134. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/y8z9a0b1c2d3_per_company_ledger_idempotency.py +0 -0
  135. {celerp-1.2.7 → celerp-1.2.8}/celerp/migrations/versions/z9a0b1c2d3e4_work_centers.py +0 -0
  136. {celerp-1.2.7 → celerp-1.2.8}/celerp/models/accounting.py +0 -0
  137. {celerp-1.2.7 → celerp-1.2.8}/celerp/models/ai.py +0 -0
  138. {celerp-1.2.7 → celerp-1.2.8}/celerp/models/auth.py +0 -0
  139. {celerp-1.2.7 → celerp-1.2.8}/celerp/models/base.py +0 -0
  140. {celerp-1.2.7 → celerp-1.2.8}/celerp/models/company.py +0 -0
  141. {celerp-1.2.7 → celerp-1.2.8}/celerp/models/connector_config.py +0 -0
  142. {celerp-1.2.7 → celerp-1.2.8}/celerp/models/import_batch.py +0 -0
  143. {celerp-1.2.7 → celerp-1.2.8}/celerp/models/ledger.py +0 -0
  144. {celerp-1.2.7 → celerp-1.2.8}/celerp/models/marketplace.py +0 -0
  145. {celerp-1.2.7 → celerp-1.2.8}/celerp/models/notification.py +0 -0
  146. {celerp-1.2.7 → celerp-1.2.8}/celerp/models/share.py +0 -0
  147. {celerp-1.2.7 → celerp-1.2.8}/celerp/models/supporter.py +0 -0
  148. {celerp-1.2.7 → celerp-1.2.8}/celerp/modules/__init__.py +0 -0
  149. {celerp-1.2.7 → celerp-1.2.8}/celerp/modules/api.py +0 -0
  150. {celerp-1.2.7 → celerp-1.2.8}/celerp/modules/audit.py +0 -0
  151. {celerp-1.2.7 → celerp-1.2.8}/celerp/modules/license.py +0 -0
  152. {celerp-1.2.7 → celerp-1.2.8}/celerp/modules/loader.py +0 -0
  153. {celerp-1.2.7 → celerp-1.2.8}/celerp/modules/references.py +0 -0
  154. {celerp-1.2.7 → celerp-1.2.8}/celerp/modules/registry.py +0 -0
  155. {celerp-1.2.7 → celerp-1.2.8}/celerp/modules/slots.py +0 -0
  156. {celerp-1.2.7 → celerp-1.2.8}/celerp/notifications/__init__.py +0 -0
  157. {celerp-1.2.7 → celerp-1.2.8}/celerp/notifications/service.py +0 -0
  158. {celerp-1.2.7 → celerp-1.2.8}/celerp/notifications/sse.py +0 -0
  159. {celerp-1.2.7 → celerp-1.2.8}/celerp/output/__init__.py +0 -0
  160. {celerp-1.2.7 → celerp-1.2.8}/celerp/output/pdf.py +0 -0
  161. {celerp-1.2.7 → celerp-1.2.8}/celerp/output/share_render.py +0 -0
  162. {celerp-1.2.7 → celerp-1.2.8}/celerp/projections/__init__.py +0 -0
  163. {celerp-1.2.7 → celerp-1.2.8}/celerp/projections/handlers/__init__.py +0 -0
  164. {celerp-1.2.7 → celerp-1.2.8}/celerp/projections/handlers/marketplace.py +0 -0
  165. {celerp-1.2.7 → celerp-1.2.8}/celerp/projections/handlers/scanning.py +0 -0
  166. {celerp-1.2.7 → celerp-1.2.8}/celerp/projections/handlers/system.py +0 -0
  167. {celerp-1.2.7 → celerp-1.2.8}/celerp/routers/__init__.py +0 -0
  168. {celerp-1.2.7 → celerp-1.2.8}/celerp/routers/auth.py +0 -0
  169. {celerp-1.2.7 → celerp-1.2.8}/celerp/routers/companies.py +0 -0
  170. {celerp-1.2.7 → celerp-1.2.8}/celerp/routers/debug.py +0 -0
  171. {celerp-1.2.7 → celerp-1.2.8}/celerp/routers/doctor.py +0 -0
  172. {celerp-1.2.7 → celerp-1.2.8}/celerp/routers/events.py +0 -0
  173. {celerp-1.2.7 → celerp-1.2.8}/celerp/routers/health.py +0 -0
  174. {celerp-1.2.7 → celerp-1.2.8}/celerp/routers/ledger.py +0 -0
  175. {celerp-1.2.7 → celerp-1.2.8}/celerp/routers/notifications.py +0 -0
  176. {celerp-1.2.7 → celerp-1.2.8}/celerp/routers/stars.py +0 -0
  177. {celerp-1.2.7 → celerp-1.2.8}/celerp/schemas/__init__.py +0 -0
  178. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/__init__.py +0 -0
  179. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/activity_redaction.py +0 -0
  180. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/attachments.py +0 -0
  181. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/auth.py +0 -0
  182. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/auto_je.py +0 -0
  183. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/backup.py +0 -0
  184. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/backup_export.py +0 -0
  185. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/backup_import.py +0 -0
  186. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/backup_repo.py +0 -0
  187. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/backup_scheduler.py +0 -0
  188. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/backup_state.py +0 -0
  189. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/demo.py +0 -0
  190. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/dev_release_guard.py +0 -0
  191. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/email.py +0 -0
  192. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/field_schema.py +0 -0
  193. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/fulfill.py +0 -0
  194. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/je_keys.py +0 -0
  195. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/landed_cost.py +0 -0
  196. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/line_measures.py +0 -0
  197. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/list_behavior.py +0 -0
  198. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/money.py +0 -0
  199. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/pick.py +0 -0
  200. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/runtime_state.py +0 -0
  201. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/session_tracker.py +0 -0
  202. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/star_cta.py +0 -0
  203. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/supporter_badge.py +0 -0
  204. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/system_health.py +0 -0
  205. {celerp-1.2.7 → celerp-1.2.8}/celerp/services/units.py +0 -0
  206. {celerp-1.2.7 → celerp-1.2.8}/celerp/session_gate.py +0 -0
  207. {celerp-1.2.7 → celerp-1.2.8}/celerp/tax_regimes.py +0 -0
  208. {celerp-1.2.7 → celerp-1.2.8}/celerp.egg-info/dependency_links.txt +0 -0
  209. {celerp-1.2.7 → celerp-1.2.8}/celerp.egg-info/entry_points.txt +0 -0
  210. {celerp-1.2.7 → celerp-1.2.8}/celerp.egg-info/requires.txt +0 -0
  211. {celerp-1.2.7 → celerp-1.2.8}/celerp.egg-info/top_level.txt +0 -0
  212. {celerp-1.2.7 → celerp-1.2.8}/default_modules/__init__.py +0 -0
  213. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-accounting/LICENSE +0 -0
  214. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-accounting/__init__.py +0 -0
  215. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-accounting/celerp_accounting/__init__.py +0 -0
  216. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-accounting/celerp_accounting/api_setup.py +0 -0
  217. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-accounting/celerp_accounting/csv_parser.py +0 -0
  218. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-accounting/celerp_accounting/matcher.py +0 -0
  219. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-accounting/celerp_accounting/models.py +0 -0
  220. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-accounting/celerp_accounting/projections.py +0 -0
  221. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-accounting/celerp_accounting/routes.py +0 -0
  222. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-accounting/celerp_accounting/ui_routes.py +0 -0
  223. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-accounting/tests/test_accounting_banking.py +0 -0
  224. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-accounting/tests/test_accounting_banking_routers.py +0 -0
  225. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-accounting/tests/test_accounting_gaps.py +0 -0
  226. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-accounting/tests/test_accounting_import_idempotency.py +0 -0
  227. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-accounting/tests/test_reconciliation_v2.py +0 -0
  228. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-admin/__init__.py +0 -0
  229. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-admin/celerp_admin/__init__.py +0 -0
  230. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-admin/celerp_admin/routes.py +0 -0
  231. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-admin/celerp_admin/setup.py +0 -0
  232. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/__init__.py +0 -0
  233. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/celerp_ai/__init__.py +0 -0
  234. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/celerp_ai/routes.py +0 -0
  235. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/celerp_ai/setup.py +0 -0
  236. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/celerp_ai/ui_routes.py +0 -0
  237. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/tests/conftest.py +0 -0
  238. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/tests/test_batch.py +0 -0
  239. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/tests/test_cleanup.py +0 -0
  240. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/tests/test_conversations.py +0 -0
  241. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/tests/test_coverage_gaps.py +0 -0
  242. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/tests/test_intent.py +0 -0
  243. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/tests/test_llm.py +0 -0
  244. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/tests/test_memory.py +0 -0
  245. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/tests/test_models.py +0 -0
  246. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/tests/test_page_count.py +0 -0
  247. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/tests/test_quota.py +0 -0
  248. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/tests/test_router.py +0 -0
  249. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/tests/test_routes_coverage.py +0 -0
  250. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/tests/test_service.py +0 -0
  251. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-ai/tests/test_tools.py +0 -0
  252. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-backup/__init__.py +0 -0
  253. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-backup/celerp_backup/__init__.py +0 -0
  254. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-backup/celerp_backup/routes.py +0 -0
  255. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-backup/celerp_backup/setup.py +0 -0
  256. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-connectors/celerp_connectors/__init__.py +0 -0
  257. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-connectors/celerp_connectors/models.py +0 -0
  258. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-connectors/tests/__init__.py +0 -0
  259. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-connectors/tests/test_images.py +0 -0
  260. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-connectors/tests/test_quickbooks.py +0 -0
  261. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-connectors/tests/test_rate_limiting.py +0 -0
  262. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-connectors/tests/test_xero.py +0 -0
  263. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-contacts/LICENSE +0 -0
  264. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-contacts/__init__.py +0 -0
  265. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-contacts/celerp_contacts/__init__.py +0 -0
  266. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-contacts/celerp_contacts/migrations.py +0 -0
  267. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-contacts/celerp_contacts/projections.py +0 -0
  268. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-contacts/celerp_contacts/routes.py +0 -0
  269. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-contacts/celerp_contacts/ui_routes.py +0 -0
  270. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-dashboard/LICENSE +0 -0
  271. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-dashboard/__init__.py +0 -0
  272. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-dashboard/celerp_dashboard/__init__.py +0 -0
  273. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-dashboard/celerp_dashboard/routes.py +0 -0
  274. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-dashboard/celerp_dashboard/setup.py +0 -0
  275. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/LICENSE +0 -0
  276. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/__init__.py +0 -0
  277. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/celerp_docs/__init__.py +0 -0
  278. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/celerp_docs/api_setup.py +0 -0
  279. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/celerp_docs/doc_constants.py +0 -0
  280. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/celerp_docs/models_share.py +0 -0
  281. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/celerp_docs/routes.py +0 -0
  282. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/celerp_docs/routes_share.py +0 -0
  283. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/celerp_docs/sequences.py +0 -0
  284. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/celerp_docs/taxes.py +0 -0
  285. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/celerp_docs/ui_routes.py +0 -0
  286. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/tests/conftest.py +0 -0
  287. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/tests/test_audits.py +0 -0
  288. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/tests/test_freight_je.py +0 -0
  289. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/tests/test_freight_lines.py +0 -0
  290. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/tests/test_fulfill_no_split.py +0 -0
  291. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/tests/test_fulfillment_activity.py +0 -0
  292. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/tests/test_inbound_je_lifecycle.py +0 -0
  293. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/tests/test_inventory_account_reconciliation.py +0 -0
  294. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/tests/test_landed_receive.py +0 -0
  295. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/tests/test_line_delete_persist.py +0 -0
  296. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/tests/test_payment_void_activity.py +0 -0
  297. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/tests/test_send_visibility.py +0 -0
  298. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-docs/tests/test_service_lines.py +0 -0
  299. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-inventory/LICENSE +0 -0
  300. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-inventory/__init__.py +0 -0
  301. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-inventory/celerp_inventory/__init__.py +0 -0
  302. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-inventory/celerp_inventory/models_import_batch.py +0 -0
  303. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-inventory/celerp_inventory/routes_attachments.py +0 -0
  304. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-inventory/celerp_inventory/routes_scanning.py +0 -0
  305. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-inventory/celerp_inventory/ui_routes.py +0 -0
  306. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-inventory/tests/test_item_recipe_projection.py +0 -0
  307. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-inventory/tests/test_item_workflow_projection.py +0 -0
  308. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-inventory/tests/test_landed_cost_projection.py +0 -0
  309. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-labels/LICENSE +0 -0
  310. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-labels/__init__.py +0 -0
  311. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-labels/celerp_labels/__init__.py +0 -0
  312. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-labels/celerp_labels/migrations/__init__.py +0 -0
  313. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-labels/celerp_labels/migrations/labels_001_create_label_templates.py +0 -0
  314. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-labels/celerp_labels/models.py +0 -0
  315. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-labels/celerp_labels/presets.py +0 -0
  316. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-labels/celerp_labels/routes.py +0 -0
  317. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-labels/celerp_labels/service.py +0 -0
  318. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-labels/celerp_labels/ui_routes.py +0 -0
  319. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-labels/requirements.txt +0 -0
  320. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-labels/tests/test_labels.py +0 -0
  321. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-labels/tests/test_labels_module.py +0 -0
  322. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/LICENSE +0 -0
  323. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/__init__.py +0 -0
  324. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/celerp_manufacturing/__init__.py +0 -0
  325. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/celerp_manufacturing/costing.py +0 -0
  326. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/celerp_manufacturing/expansion.py +0 -0
  327. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/celerp_manufacturing/labor.py +0 -0
  328. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/celerp_manufacturing/migrations.py +0 -0
  329. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/celerp_manufacturing/projection_handler.py +0 -0
  330. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/celerp_manufacturing/routes.py +0 -0
  331. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/celerp_manufacturing/ui_routes.py +0 -0
  332. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/requirements.txt +0 -0
  333. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_bom_removed.py +0 -0
  334. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_build_api.py +0 -0
  335. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_item_recipe_api.py +0 -0
  336. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_item_workflow_api.py +0 -0
  337. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_labor_providers.py +0 -0
  338. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_manufacture_from_doc.py +0 -0
  339. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_manufacturing.py +0 -0
  340. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_manufacturing_import_idempotency.py +0 -0
  341. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_manufacturing_module.py +0 -0
  342. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_mfg_events.py +0 -0
  343. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_phase2_manufacturing_validations.py +0 -0
  344. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_production_order.py +0 -0
  345. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_recipe_costing.py +0 -0
  346. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_recipe_expansion.py +0 -0
  347. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_recost_api.py +0 -0
  348. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_to_make_allocation.py +0 -0
  349. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_where_used.py +0 -0
  350. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_work_centers_and_settings.py +0 -0
  351. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-manufacturing/tests/test_workflow_time.py +0 -0
  352. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-reports/LICENSE +0 -0
  353. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-reports/__init__.py +0 -0
  354. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-reports/celerp_reports/__init__.py +0 -0
  355. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-reports/celerp_reports/api_setup.py +0 -0
  356. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-reports/celerp_reports/routes.py +0 -0
  357. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-reports/celerp_reports/ui_routes.py +0 -0
  358. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-reports/tests/test_reports_full.py +0 -0
  359. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-reports/tests/test_reports_unit.py +0 -0
  360. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-subscriptions/LICENSE +0 -0
  361. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-subscriptions/__init__.py +0 -0
  362. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-subscriptions/celerp_subscriptions/__init__.py +0 -0
  363. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-subscriptions/celerp_subscriptions/routes.py +0 -0
  364. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-subscriptions/celerp_subscriptions/ui_routes.py +0 -0
  365. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-subscriptions/tests/test_subscriptions.py +0 -0
  366. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-subscriptions/tests/test_subscriptions_ui.py +0 -0
  367. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/LICENSE +0 -0
  368. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/__init__.py +0 -0
  369. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/__init__.py +0 -0
  370. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/accessory_fashion.json +0 -0
  371. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/activewear.json +0 -0
  372. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/art_photography.json +0 -0
  373. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/audio_equipment.json +0 -0
  374. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/bag_handbag.json +0 -0
  375. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/banknote.json +0 -0
  376. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/beauty_tool.json +0 -0
  377. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/bed_bedroom.json +0 -0
  378. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/beer.json +0 -0
  379. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/beverage_nonalc.json +0 -0
  380. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/body_part_auto.json +0 -0
  381. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/book.json +0 -0
  382. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/bottoms.json +0 -0
  383. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/brake_suspension.json +0 -0
  384. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/bullion_coin.json +0 -0
  385. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/camera.json +0 -0
  386. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/colored_stone.json +0 -0
  387. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/comic.json +0 -0
  388. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/commercial_space.json +0 -0
  389. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/component_part.json +0 -0
  390. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/confectionery.json +0 -0
  391. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/consulting_service.json +0 -0
  392. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/decorative_art.json +0 -0
  393. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/diamond.json +0 -0
  394. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/drawing.json +0 -0
  395. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/dress_jumpsuit.json +0 -0
  396. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/electrical_auto.json +0 -0
  397. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/electrical_component.json +0 -0
  398. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/emerald.json +0 -0
  399. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/engine_part.json +0 -0
  400. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/fastener.json +0 -0
  401. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/fertilizer_chemical.json +0 -0
  402. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/film_video.json +0 -0
  403. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/fine_writing_instrument.json +0 -0
  404. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/fluid_lubricant.json +0 -0
  405. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/footwear.json +0 -0
  406. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/fragrance.json +0 -0
  407. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/fresh_food.json +0 -0
  408. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/fresh_produce.json +0 -0
  409. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/frozen_food.json +0 -0
  410. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/gaming_console.json +0 -0
  411. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/gold_bullion.json +0 -0
  412. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/grain_cereal.json +0 -0
  413. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/haircare.json +0 -0
  414. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/hand_tool.json +0 -0
  415. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/ingredient_bulk.json +0 -0
  416. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/interior_part.json +0 -0
  417. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/jewelry.json +0 -0
  418. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/kitchen_dining.json +0 -0
  419. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/laptop.json +0 -0
  420. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/lighting.json +0 -0
  421. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/livestock_feed.json +0 -0
  422. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/makeup.json +0 -0
  423. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/measuring_instrument.json +0 -0
  424. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/medal_token.json +0 -0
  425. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/mineral_specimen.json +0 -0
  426. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/mobile_phone.json +0 -0
  427. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/music_cd.json +0 -0
  428. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/music_vinyl.json +0 -0
  429. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/nail.json +0 -0
  430. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/numismatic_coin.json +0 -0
  431. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/outdoor_furniture.json +0 -0
  432. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/outerwear.json +0 -0
  433. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/packaged_food.json +0 -0
  434. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/painting.json +0 -0
  435. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/parking_bay.json +0 -0
  436. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/pearl.json +0 -0
  437. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/personal_care.json +0 -0
  438. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/pipe_plumbing.json +0 -0
  439. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/platinum_bullion.json +0 -0
  440. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/power_tool.json +0 -0
  441. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/print_edition.json +0 -0
  442. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/residential_unit.json +0 -0
  443. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/rough_gemstone.json +0 -0
  444. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/ruby.json +0 -0
  445. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/saas_plan.json +0 -0
  446. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/safety_ppe.json +0 -0
  447. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/sapphire.json +0 -0
  448. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/sculpture.json +0 -0
  449. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/seating.json +0 -0
  450. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/seeds.json +0 -0
  451. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/silver_bullion.json +0 -0
  452. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/skincare.json +0 -0
  453. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/soft_furnishing.json +0 -0
  454. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/software_addon.json +0 -0
  455. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/software_license.json +0 -0
  456. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/spirit.json +0 -0
  457. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/storage_furniture.json +0 -0
  458. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/swimwear.json +0 -0
  459. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/table_desk.json +0 -0
  460. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/tablet.json +0 -0
  461. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/tire_wheel.json +0 -0
  462. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/tops.json +0 -0
  463. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/trading_card.json +0 -0
  464. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/tv_display.json +0 -0
  465. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/video_game.json +0 -0
  466. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/watch.json +0 -0
  467. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/watch_strap.json +0 -0
  468. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/categories/wine.json +0 -0
  469. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/presets/agricultural.json +0 -0
  470. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/presets/artwork.json +0 -0
  471. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/presets/automotive.json +0 -0
  472. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/presets/blank.json +0 -0
  473. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/presets/books_media.json +0 -0
  474. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/presets/coins_precious_metals.json +0 -0
  475. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/presets/consulting.json +0 -0
  476. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/presets/cosmetics.json +0 -0
  477. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/presets/electronics.json +0 -0
  478. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/presets/fashion.json +0 -0
  479. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/presets/food_beverage.json +0 -0
  480. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/presets/furniture.json +0 -0
  481. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/presets/gemstones.json +0 -0
  482. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/presets/hardware.json +0 -0
  483. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/presets/property_rental.json +0 -0
  484. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/presets/saas.json +0 -0
  485. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/presets/watches.json +0 -0
  486. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/presets/wine_spirits.json +0 -0
  487. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/routes.py +0 -0
  488. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/celerp_verticals/ui_routes.py +0 -0
  489. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/tests/test_verticals.py +0 -0
  490. {celerp-1.2.7 → celerp-1.2.8}/default_modules/celerp-verticals/tests/test_verticals_routers.py +0 -0
  491. {celerp-1.2.7 → celerp-1.2.8}/legal/OFFICIAL-COMPONENTS.md +0 -0
  492. {celerp-1.2.7 → celerp-1.2.8}/legal/TRADEMARK.md +0 -0
  493. {celerp-1.2.7 → celerp-1.2.8}/pyproject.toml +0 -0
  494. {celerp-1.2.7 → celerp-1.2.8}/setup.cfg +0 -0
  495. {celerp-1.2.7 → celerp-1.2.8}/test_helpers.py +0 -0
  496. {celerp-1.2.7 → celerp-1.2.8}/ui/__init__.py +0 -0
  497. {celerp-1.2.7 → celerp-1.2.8}/ui/app.py +0 -0
  498. {celerp-1.2.7 → celerp-1.2.8}/ui/components/__init__.py +0 -0
  499. {celerp-1.2.7 → celerp-1.2.8}/ui/components/activity.py +0 -0
  500. {celerp-1.2.7 → celerp-1.2.8}/ui/components/backup.py +0 -0
  501. {celerp-1.2.7 → celerp-1.2.8}/ui/components/cloud_gate.py +0 -0
  502. {celerp-1.2.7 → celerp-1.2.8}/ui/components/currency.py +0 -0
  503. {celerp-1.2.7 → celerp-1.2.8}/ui/components/files.py +0 -0
  504. {celerp-1.2.7 → celerp-1.2.8}/ui/components/notes.py +0 -0
  505. {celerp-1.2.7 → celerp-1.2.8}/ui/components/phone.py +0 -0
  506. {celerp-1.2.7 → celerp-1.2.8}/ui/components/shell.py +0 -0
  507. {celerp-1.2.7 → celerp-1.2.8}/ui/config.py +0 -0
  508. {celerp-1.2.7 → celerp-1.2.8}/ui/i18n.py +0 -0
  509. {celerp-1.2.7 → celerp-1.2.8}/ui/locales/ar.json +4 -4
  510. {celerp-1.2.7 → celerp-1.2.8}/ui/locales/de.json +4 -4
  511. {celerp-1.2.7 → celerp-1.2.8}/ui/locales/es.json +4 -4
  512. {celerp-1.2.7 → celerp-1.2.8}/ui/locales/fr.json +4 -4
  513. {celerp-1.2.7 → celerp-1.2.8}/ui/locales/id.json +4 -4
  514. {celerp-1.2.7 → celerp-1.2.8}/ui/locales/it.json +4 -4
  515. {celerp-1.2.7 → celerp-1.2.8}/ui/locales/ja.json +4 -4
  516. {celerp-1.2.7 → celerp-1.2.8}/ui/locales/pt.json +4 -4
  517. {celerp-1.2.7 → celerp-1.2.8}/ui/locales/th.json +4 -4
  518. {celerp-1.2.7 → celerp-1.2.8}/ui/locales/vi.json +4 -4
  519. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/__init__.py +0 -0
  520. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/accounting.py +0 -0
  521. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/accounting_import.py +0 -0
  522. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/contacts.py +0 -0
  523. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/csv_import.py +0 -0
  524. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/dashboard.py +0 -0
  525. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/docs_import.py +0 -0
  526. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/documents.py +0 -0
  527. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/events.py +0 -0
  528. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/lists_import.py +0 -0
  529. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/manufacturing.py +0 -0
  530. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/notifications.py +0 -0
  531. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/reconciliation.py +0 -0
  532. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/reports.py +0 -0
  533. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/search.py +0 -0
  534. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/settings.py +0 -0
  535. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/settings_accounting.py +0 -0
  536. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/settings_cloud.py +0 -0
  537. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/settings_contacts.py +0 -0
  538. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/settings_general.py +0 -0
  539. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/settings_import.py +0 -0
  540. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/settings_inventory.py +0 -0
  541. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/settings_manufacturing.py +0 -0
  542. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/settings_purchasing.py +0 -0
  543. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/settings_sales.py +0 -0
  544. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/setup.py +0 -0
  545. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/stars.py +0 -0
  546. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/subscriptions.py +0 -0
  547. {celerp-1.2.7 → celerp-1.2.8}/ui/routes/subscriptions_import.py +0 -0
  548. {celerp-1.2.7 → celerp-1.2.8}/ui/static/htmx.min.js +0 -0
  549. {celerp-1.2.7 → celerp-1.2.8}/ui/static/icon.png +0 -0
  550. {celerp-1.2.7 → celerp-1.2.8}/ui/static/logo.png +0 -0
  551. {celerp-1.2.7 → celerp-1.2.8}/ui/static/vendor/intl-tel-input/img/flags.webp +0 -0
  552. {celerp-1.2.7 → celerp-1.2.8}/ui/static/vendor/intl-tel-input/img/flags@2x.webp +0 -0
  553. {celerp-1.2.7 → celerp-1.2.8}/ui/static/vendor/intl-tel-input/intlTelInput.min.css +0 -0
  554. {celerp-1.2.7 → celerp-1.2.8}/ui/static/vendor/intl-tel-input/intlTelInput.min.js +0 -0
  555. {celerp-1.2.7 → celerp-1.2.8}/ui/static/vendor/intl-tel-input/intlTelInputWithUtils.min.js +0 -0
  556. {celerp-1.2.7 → celerp-1.2.8}/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: 1.2.7
3
+ Version: 1.2.8
4
4
  Summary: Free, self-hosted ERP for small businesses
5
5
  License: Business Source License 1.1
6
6
 
@@ -9,7 +9,7 @@ CelERP relay service and injected at call time via ConnectorContext.
9
9
 
10
10
  Sync direction:
11
11
  - INBOUND: external platform -> Celerp (pull products, orders, contacts)
12
- - OUTBOUND: Celerp -> external platform (push invoices, inventory updates)
12
+ - OUTBOUND: Celerp -> external platform (push items, invoices, inventory)
13
13
  - BOTH: bidirectional
14
14
  """
15
15
  from __future__ import annotations
@@ -49,7 +49,8 @@ class SyncFrequency(str, Enum):
49
49
  DAILY = "daily" # once per day at configured hour
50
50
 
51
51
 
52
- # Entity classification for direction filtering
52
+ # Entity classification for direction filtering. Inbound entities are pulled from
53
+ # the platform; the *_out entities are pushed from Celerp to the platform.
53
54
  _INBOUND_ENTITIES = {"products", "orders", "contacts", "inventory"}
54
55
  _OUTBOUND_ENTITIES = {"products_out", "invoices_out", "inventory_out"}
55
56
 
@@ -77,7 +78,7 @@ class ConnectorContext:
77
78
  @dataclass
78
79
  class SyncResult:
79
80
  entity: SyncEntity
80
- direction: SyncDirection
81
+ direction: SyncDirection = SyncDirection.INBOUND
81
82
  created: int = 0
82
83
  updated: int = 0
83
84
  skipped: int = 0
@@ -87,9 +88,18 @@ class SyncResult:
87
88
  def ok(self) -> bool:
88
89
  return not self.errors
89
90
 
91
+ def record(self, outcome: str) -> None:
92
+ """Tally a `connector_upsert`/push outcome ("created" | "updated" | "noop")."""
93
+ if outcome == "created":
94
+ self.created += 1
95
+ elif outcome == "updated":
96
+ self.updated += 1
97
+ else:
98
+ self.skipped += 1
99
+
90
100
 
91
101
  class ConnectorBase(ABC):
92
- """Abstract base for all platform connectors."""
102
+ """Abstract base for all platform connectors (inbound + outbound)."""
93
103
 
94
104
  name: str
95
105
  display_name: str
@@ -109,8 +119,8 @@ class ConnectorBase(ABC):
109
119
  ...
110
120
 
111
121
  async def sync_inventory(self, ctx: ConnectorContext, since: datetime | None = None) -> SyncResult:
112
- """Push Celerp inventory levels -> platform. Override if supported."""
113
- raise NotImplementedError(f"{self.name} does not support inventory push")
122
+ """Pull inventory levels from platform -> Celerp. Override if supported."""
123
+ raise NotImplementedError(f"{self.name} does not support inventory sync")
114
124
 
115
125
  async def sync_contacts(self, ctx: ConnectorContext, since: datetime | None = None) -> SyncResult:
116
126
  """Pull customers/vendors from platform -> Celerp CRM. Override if supported."""
@@ -130,7 +140,7 @@ class ConnectorBase(ABC):
130
140
 
131
141
  # -- Webhook lifecycle (override for e-commerce connectors) ----------------
132
142
 
133
- async def register_webhooks(self, ctx: ConnectorContext, webhook_url: str) -> list[str]:
143
+ async def register_webhooks(self, ctx: ConnectorContext, webhook_url: str, secret: str | None = None) -> list[str]:
134
144
  """Register platform webhooks. Returns list of webhook IDs. Override if supported."""
135
145
  return []
136
146
 
@@ -16,11 +16,28 @@ from typing import Awaitable, Callable
16
16
 
17
17
  import sqlalchemy as sa
18
18
 
19
- from celerp.connectors.base import ConnectorCategory, SyncDirection, SyncFrequency
19
+ from celerp.connectors.base import SyncDirection
20
20
  from celerp.models.connector_config import ConnectorConfig
21
21
 
22
22
  log = logging.getLogger(__name__)
23
23
 
24
+ # Outbound entity -> connector method. A connector "supports" an outbound entity when it
25
+ # overrides the base (which raises NotImplementedError); we only dispatch those, so the
26
+ # scheduler never emits a failed run for a push a connector doesn't implement.
27
+ _OUTBOUND_ENTITY_METHODS = {
28
+ "products_out": "sync_products_out",
29
+ "invoices_out": "sync_invoices_out",
30
+ "inventory_out": "sync_inventory_out",
31
+ }
32
+
33
+
34
+ def _supported_outbound(connector) -> list[str]:
35
+ from celerp.connectors.base import ConnectorBase
36
+ return [
37
+ entity for entity, method in _OUTBOUND_ENTITY_METHODS.items()
38
+ if getattr(type(connector), method, None) is not getattr(ConnectorBase, method, None)
39
+ ]
40
+
24
41
  _CHECK_INTERVAL_SECONDS = 3600 # check every hour
25
42
  _MIN_HOURS_BETWEEN_SYNCS = 23
26
43
 
@@ -81,8 +98,7 @@ async def check_and_run_daily_syncs(
81
98
  )
82
99
  continue
83
100
 
84
- direction = SyncDirection(config.direction)
85
- log.info("daily_scheduler: running %s (direction=%s)", config.connector, direction.value)
101
+ log.info("daily_scheduler: running %s", config.connector)
86
102
 
87
103
  try:
88
104
  ctx = await token_fetcher(company_id, config.connector)
@@ -90,16 +106,29 @@ async def check_and_run_daily_syncs(
90
106
  log.warning("daily_scheduler: token fetch failed for %s: %s", config.connector, exc)
91
107
  continue
92
108
 
93
- # Run sync for all supported entities respecting direction
94
- for entity_enum in connector.supported_entities:
109
+ # Sync every supported entity, honouring the configured direction: inbound
110
+ # entities pull from the platform; outbound (*_out) entities push back the ones
111
+ # the connector actually implements. run_sync enforces the direction gate too.
112
+ direction = SyncDirection(config.direction)
113
+ entities = [e.value for e in connector.supported_entities]
114
+ if direction in (SyncDirection.BOTH, SyncDirection.OUTBOUND):
115
+ entities += _supported_outbound(connector)
116
+ entity_results = []
117
+ for entity in entities:
95
118
  try:
96
- await run_sync(connector, ctx, entity_enum.value, direction=direction)
119
+ entity_results.append(await run_sync(connector, ctx, entity, direction=direction))
97
120
  except Exception as exc:
98
- log.error("daily_scheduler: sync error %s/%s: %s", config.connector, entity_enum.value, exc)
121
+ log.error("daily_scheduler: sync error %s/%s: %s", config.connector, entity, exc)
122
+
123
+ # Only mark the connector synced (advancing the daily clock) if at least one
124
+ # entity made progress. On a total failure (e.g. a transient outage) we leave
125
+ # last_daily_sync_at unset, so the connector stays "due" and retries at the next
126
+ # tick that lands on its configured hour (the next day, or sooner on app restart).
127
+ if not any((r.created or r.updated or not r.errors) for r in entity_results):
128
+ log.warning("daily_scheduler: all entities failed for %s — will retry when next due", config.connector)
129
+ continue
99
130
 
100
131
  synced.append(config.connector)
101
-
102
- # Update last_daily_sync_at only after actually running
103
132
  async with get_session_ctx() as session:
104
133
  await session.execute(
105
134
  sa.update(ConnectorConfig)
@@ -45,9 +45,25 @@ class RateLimitedClient:
45
45
  async def delete(self, url: str, **kwargs) -> httpx.Response:
46
46
  return await self._request("DELETE", url, **kwargs)
47
47
 
48
+ # Transient transport failures (flaky network, mid-pagination drops) — retried
49
+ # with the same backoff as 429/503 rather than aborting the whole sync page.
50
+ _RETRY_EXC = (
51
+ httpx.ConnectError, httpx.ReadTimeout, httpx.WriteTimeout,
52
+ httpx.PoolTimeout, httpx.RemoteProtocolError,
53
+ )
54
+
48
55
  async def _request(self, method: str, url: str, **kwargs) -> httpx.Response:
49
56
  for attempt in range(self._max_retries + 1):
50
- resp = await self._client.request(method, url, **kwargs)
57
+ try:
58
+ resp = await self._client.request(method, url, **kwargs)
59
+ except self._RETRY_EXC as exc:
60
+ if attempt == self._max_retries:
61
+ raise
62
+ delay = self._backoff_base ** attempt
63
+ log.info("Transport error (%s), retry %d/%d in %.1fs",
64
+ type(exc).__name__, attempt + 1, self._max_retries, delay)
65
+ await asyncio.sleep(delay)
66
+ continue
51
67
  if resp.status_code not in (429, 503):
52
68
  return resp
53
69
  if attempt == self._max_retries:
@@ -100,14 +100,13 @@ async def _query(ctx: ConnectorContext, sql: str) -> list[dict[str, Any]]:
100
100
  class QuickBooksConnector(ConnectorBase):
101
101
  name = "quickbooks"
102
102
  display_name = "QuickBooks"
103
- supported_entities = [SyncEntity.PRODUCTS, SyncEntity.ORDERS, SyncEntity.CONTACTS, SyncEntity.INVOICES]
104
- direction = SyncDirection.BOTH
103
+ supported_entities = [SyncEntity.PRODUCTS, SyncEntity.ORDERS, SyncEntity.CONTACTS]
105
104
  category = ConnectorCategory.ACCOUNTING
105
+ direction = SyncDirection.BOTH
106
106
  conflict_strategy = {
107
107
  SyncEntity.PRODUCTS: "newest",
108
108
  SyncEntity.ORDERS: "platform",
109
109
  SyncEntity.CONTACTS: "merge",
110
- SyncEntity.INVOICES: "platform",
111
110
  }
112
111
 
113
112
  # -- Products (Items in QB) ------------------------------------------------
@@ -124,7 +123,7 @@ class QuickBooksConnector(ConnectorBase):
124
123
  Item.PurchaseCost -> item.cost_price
125
124
  Item.Id -> idempotency_key
126
125
  """
127
- result = SyncResult(entity=SyncEntity.PRODUCTS, direction=SyncDirection.INBOUND)
126
+ result = SyncResult(entity=SyncEntity.PRODUCTS)
128
127
  errors: list[str] = []
129
128
 
130
129
  try:
@@ -159,11 +158,7 @@ class QuickBooksConnector(ConnectorBase):
159
158
  cost_price=money(qb_item.get("PurchaseCost")),
160
159
  idempotency_key=idempotency_key,
161
160
  )
162
- created = await _upsert.upsert_item(ctx.company_id, item)
163
- if created:
164
- result.created += 1
165
- else:
166
- result.skipped += 1
161
+ result.record(await _upsert.upsert_item(ctx.company_id, item))
167
162
  except Exception as exc:
168
163
  errors.append(f"SKU {sku}: {exc}")
169
164
 
@@ -187,7 +182,7 @@ class QuickBooksConnector(ConnectorBase):
187
182
  Invoice.Balance -> used to determine status
188
183
  Invoice.Id -> idempotency_key
189
184
  """
190
- result = SyncResult(entity=SyncEntity.ORDERS, direction=SyncDirection.INBOUND)
185
+ result = SyncResult(entity=SyncEntity.ORDERS)
191
186
  errors: list[str] = []
192
187
 
193
188
  try:
@@ -199,11 +194,7 @@ class QuickBooksConnector(ConnectorBase):
199
194
 
200
195
  for inv in invoices:
201
196
  try:
202
- created = await _upsert.upsert_invoice_from_quickbooks(ctx.company_id, inv)
203
- if created:
204
- result.created += 1
205
- else:
206
- result.skipped += 1
197
+ result.record(await _upsert.upsert_invoice_from_quickbooks(ctx.company_id, inv))
207
198
  except Exception as exc:
208
199
  errors.append(f"Invoice {inv.get('DocNumber')}: {exc}")
209
200
 
@@ -218,7 +209,7 @@ class QuickBooksConnector(ConnectorBase):
218
209
 
219
210
  async def sync_contacts(self, ctx: ConnectorContext, since: datetime | None = None) -> SyncResult:
220
211
  """Pull QuickBooks Customers -> Celerp CRM contacts."""
221
- result = SyncResult(entity=SyncEntity.CONTACTS, direction=SyncDirection.INBOUND)
212
+ result = SyncResult(entity=SyncEntity.CONTACTS)
222
213
  errors: list[str] = []
223
214
 
224
215
  try:
@@ -230,11 +221,7 @@ class QuickBooksConnector(ConnectorBase):
230
221
 
231
222
  for customer in customers:
232
223
  try:
233
- created = await _upsert.upsert_contact_from_quickbooks(ctx.company_id, customer)
234
- if created:
235
- result.created += 1
236
- else:
237
- result.skipped += 1
224
+ result.record(await _upsert.upsert_contact_from_quickbooks(ctx.company_id, customer))
238
225
  except Exception as exc:
239
226
  errors.append(f"Customer {customer.get('Id')}: {exc}")
240
227
 
@@ -286,6 +273,11 @@ class QuickBooksConnector(ConnectorBase):
286
273
  json=payload,
287
274
  )
288
275
  resp.raise_for_status()
276
+ # Write-back: stamp the returned QB invoice id so the next run's
277
+ # list_unsynced_invoices skips this doc — never created in QB twice.
278
+ qb_id = ((resp.json() or {}).get("Invoice") or {}).get("Id")
279
+ if qb_id and inv.get("entity_id"):
280
+ await _upsert.mark_doc_pushed(ctx.company_id, inv["entity_id"], "quickbooks", qb_id)
289
281
  result.created += 1
290
282
  except Exception as exc:
291
283
  errors.append(f"Invoice {inv.get('ref_id')}: {exc}")
@@ -58,8 +58,8 @@ class ShopifyConnector(ConnectorBase):
58
58
  name = "shopify"
59
59
  display_name = "Shopify"
60
60
  supported_entities = [SyncEntity.PRODUCTS, SyncEntity.ORDERS, SyncEntity.CONTACTS]
61
- direction = SyncDirection.BOTH
62
61
  category = ConnectorCategory.WEBSITE
62
+ direction = SyncDirection.BOTH
63
63
  conflict_strategy = {
64
64
  SyncEntity.PRODUCTS: "newest",
65
65
  SyncEntity.ORDERS: "platform",
@@ -99,7 +99,7 @@ class ShopifyConnector(ConnectorBase):
99
99
  """
100
100
  from celerp_inventory.routes import ItemCreate
101
101
 
102
- result = SyncResult(entity=SyncEntity.PRODUCTS, direction=SyncDirection.INBOUND)
102
+ result = SyncResult(entity=SyncEntity.PRODUCTS)
103
103
  errors: list[str] = []
104
104
 
105
105
  try:
@@ -132,11 +132,7 @@ class ShopifyConnector(ConnectorBase):
132
132
  )
133
133
 
134
134
  try:
135
- created = await _upsert.upsert_item(ctx.company_id, item)
136
- if created:
137
- result.created += 1
138
- else:
139
- result.skipped += 1
135
+ result.record(await _upsert.upsert_item(ctx.company_id, item))
140
136
  except Exception as exc:
141
137
  errors.append(f"SKU {sku}: {exc}")
142
138
  continue
@@ -203,7 +199,7 @@ class ShopifyConnector(ConnectorBase):
203
199
  financial_status -> doc status (paid -> closed, pending -> open)
204
200
  order.id -> idempotency_key
205
201
  """
206
- result = SyncResult(entity=SyncEntity.ORDERS, direction=SyncDirection.INBOUND)
202
+ result = SyncResult(entity=SyncEntity.ORDERS)
207
203
  errors: list[str] = []
208
204
 
209
205
  try:
@@ -217,11 +213,7 @@ class ShopifyConnector(ConnectorBase):
217
213
 
218
214
  for order in orders:
219
215
  try:
220
- created = await _upsert.upsert_order_from_shopify(ctx.company_id, order)
221
- if created:
222
- result.created += 1
223
- else:
224
- result.skipped += 1
216
+ result.record(await _upsert.upsert_order_from_shopify(ctx.company_id, order))
225
217
  except Exception as exc:
226
218
  msg = f"Order {order.get('name')}: {exc}"
227
219
  log.warning("shopify.sync_orders error: %s", msg)
@@ -238,7 +230,7 @@ class ShopifyConnector(ConnectorBase):
238
230
 
239
231
  async def sync_contacts(self, ctx: ConnectorContext, since: datetime | None = None) -> SyncResult:
240
232
  """Pull Shopify customers -> Celerp CRM contacts."""
241
- result = SyncResult(entity=SyncEntity.CONTACTS, direction=SyncDirection.INBOUND)
233
+ result = SyncResult(entity=SyncEntity.CONTACTS)
242
234
  errors: list[str] = []
243
235
 
244
236
  try:
@@ -249,11 +241,7 @@ class ShopifyConnector(ConnectorBase):
249
241
 
250
242
  for customer in customers:
251
243
  try:
252
- created = await _upsert.upsert_contact_from_shopify(ctx.company_id, customer)
253
- if created:
254
- result.created += 1
255
- else:
256
- result.skipped += 1
244
+ result.record(await _upsert.upsert_contact_from_shopify(ctx.company_id, customer))
257
245
  except Exception as exc:
258
246
  errors.append(f"Customer {customer.get('id')}: {exc}")
259
247
 
@@ -262,8 +250,13 @@ class ShopifyConnector(ConnectorBase):
262
250
 
263
251
  # -- Outbound: Inventory push ----------------------------------------------
264
252
 
265
- async def sync_inventory(self, ctx: ConnectorContext, since: datetime | None = None) -> SyncResult:
266
- """Push Celerp stock levels -> Shopify inventory levels."""
253
+ async def sync_inventory_out(self, ctx: ConnectorContext) -> SyncResult:
254
+ """Push Celerp stock levels -> Shopify inventory levels (outbound).
255
+
256
+ Named *_out so it is classified as an outbound entity and actually gets
257
+ dispatched by the scheduler; as `sync_inventory` it was mis-typed as the
258
+ inbound 'inventory' entity and never ran (Shopify has no inbound inventory
259
+ pull — stock arrives via product variants)."""
267
260
  result = SyncResult(entity=SyncEntity.INVENTORY, direction=SyncDirection.OUTBOUND)
268
261
  errors: list[str] = []
269
262
 
@@ -376,47 +369,6 @@ class ShopifyConnector(ConnectorBase):
376
369
  )
377
370
  return result
378
371
 
379
- # -- Webhook lifecycle -----------------------------------------------------
380
-
381
- _INBOUND_TOPICS = [
382
- "products/create", "products/update", "products/delete",
383
- "orders/create", "orders/updated",
384
- "inventory_levels/update",
385
- "customers/create", "customers/update",
386
- ]
387
-
388
- def webhook_topics_for_direction(self, direction: SyncDirection) -> list[str]:
389
- if direction == SyncDirection.OUTBOUND:
390
- return []
391
- return self._INBOUND_TOPICS
392
-
393
- async def register_webhooks(self, ctx: ConnectorContext, webhook_url: str) -> list[str]:
394
- """Register Shopify webhooks. Returns list of webhook IDs."""
395
- base = _base_url(ctx)
396
- ids: list[str] = []
397
- topics = self.webhook_topics_for_direction(self.direction)
398
- async with RateLimitedClient() as client:
399
- for topic in topics:
400
- resp = await client.post(
401
- f"{base}/webhooks.json",
402
- headers=_headers(ctx),
403
- json={"webhook": {"topic": topic, "address": webhook_url, "format": "json"}},
404
- )
405
- if resp.status_code in (200, 201):
406
- wh = resp.json().get("webhook", {})
407
- ids.append(str(wh.get("id", "")))
408
- else:
409
- log.warning("shopify.register_webhook topic=%s status=%d", topic, resp.status_code)
410
- return ids
411
-
412
- async def deregister_webhooks(self, ctx: ConnectorContext, webhook_ids: list[str]) -> None:
413
- base = _base_url(ctx)
414
- async with RateLimitedClient() as client:
415
- for wid in webhook_ids:
416
- resp = await client.delete(f"{base}/webhooks/{wid}.json", headers=_headers(ctx))
417
- if resp.status_code not in (200, 204):
418
- log.warning("shopify.deregister_webhook id=%s status=%d", wid, resp.status_code)
419
-
420
372
 
421
373
  # -- Pagination helper ---------------------------------------------------------
422
374
 
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
 
6
6
  import json
7
7
  import logging
8
- from datetime import datetime, timezone
8
+ from datetime import datetime, timedelta, timezone
9
9
 
10
10
  from celerp.connectors.base import (
11
11
  ConnectorBase,
@@ -53,10 +53,90 @@ async def _last_success_watermark(company_id: str, connector: str, entity: str):
53
53
  SyncRun.status == "success",
54
54
  )
55
55
  )
56
- except Exception:
56
+ except Exception as exc:
57
+ # A read failure degrades to a full re-pull (safe, dup-safe via idempotency
58
+ # keys) — but say so rather than silently widening every sync.
59
+ log.warning("Could not read sync watermark for %s.%s: %s — full pull", connector, entity, exc)
60
+ return None
61
+
62
+
63
+ # Concurrency guard: a still-unfinished run older than this is treated as dead (the
64
+ # process likely crashed mid-sync), so a new run is allowed to supersede it.
65
+ _STALE_AFTER = timedelta(minutes=15)
66
+ _BUSY = "busy"
67
+
68
+
69
+ async def _begin_run(company_id: str, connector: str, entity: str, direction_str: str, started_at):
70
+ """Insert an in-progress SyncRun (status=running, finished_at=None) so the UI can
71
+ observe an active sync, and return its id. Returns _BUSY if a recent unfinished run
72
+ for this (company, connector, entity) already exists (the concurrency guard). Returns
73
+ None if the row could not be written, in which case the sync still runs and a single
74
+ final row is written by _finish_run."""
75
+ import sqlalchemy as sa
76
+
77
+ from celerp.db import get_session_ctx
78
+
79
+ try:
80
+ async with get_session_ctx() as session:
81
+ existing = await session.scalar(
82
+ sa.select(SyncRun.id)
83
+ .where(
84
+ SyncRun.company_id == company_id,
85
+ SyncRun.connector == connector,
86
+ SyncRun.entity == entity,
87
+ SyncRun.finished_at.is_(None),
88
+ SyncRun.started_at >= started_at - _STALE_AFTER,
89
+ )
90
+ .limit(1)
91
+ )
92
+ if existing is not None:
93
+ return _BUSY
94
+ run = SyncRun(
95
+ company_id=company_id, connector=connector, entity=entity,
96
+ direction=direction_str, started_at=started_at, finished_at=None,
97
+ created_count=0, updated_count=0, skipped_count=0,
98
+ errors_json=None, status="running",
99
+ )
100
+ session.add(run)
101
+ await session.commit()
102
+ await session.refresh(run)
103
+ return run.id
104
+ except Exception as exc:
105
+ log.warning("Failed to record in-progress SyncRun: %s", exc)
57
106
  return None
58
107
 
59
108
 
109
+ async def _finish_run(run_id, company_id, connector, entity, result, started_at, finished_at, status):
110
+ """Update the in-progress row with the final outcome, or insert a final row if the
111
+ in-progress write failed (run_id is None)."""
112
+ import sqlalchemy as sa
113
+
114
+ from celerp.db import get_session_ctx
115
+
116
+ direction_str = result.direction.value if hasattr(result.direction, "value") else str(result.direction)
117
+ errors_json = json.dumps(result.errors) if result.errors else None
118
+ try:
119
+ async with get_session_ctx() as session:
120
+ if run_id is not None:
121
+ await session.execute(
122
+ sa.update(SyncRun).where(SyncRun.id == run_id).values(
123
+ direction=direction_str, finished_at=finished_at,
124
+ created_count=result.created, updated_count=result.updated,
125
+ skipped_count=result.skipped, errors_json=errors_json, status=status,
126
+ )
127
+ )
128
+ else:
129
+ session.add(SyncRun(
130
+ company_id=company_id, connector=connector, entity=entity,
131
+ direction=direction_str, started_at=started_at, finished_at=finished_at,
132
+ created_count=result.created, updated_count=result.updated,
133
+ skipped_count=result.skipped, errors_json=errors_json, status=status,
134
+ ))
135
+ await session.commit()
136
+ except Exception as exc:
137
+ log.warning("Failed to record SyncRun: %s", exc)
138
+
139
+
60
140
  async def run_sync(
61
141
  connector: ConnectorBase,
62
142
  ctx: ConnectorContext,
@@ -69,8 +149,6 @@ async def run_sync(
69
149
  If ``direction`` is provided, checks whether ``entity`` is allowed
70
150
  for that direction before running. Returns a failed SyncResult if blocked.
71
151
  """
72
- from celerp.db import get_session_ctx
73
-
74
152
  # Direction gate
75
153
  if direction and not entity_allowed(entity, direction):
76
154
  try:
@@ -92,13 +170,25 @@ async def run_sync(
92
170
  if sync_method is None:
93
171
  raise ValueError(f"{connector.name} has no method {method_name}")
94
172
 
173
+ started_at = datetime.now(timezone.utc)
174
+ intended_direction = direction if isinstance(direction, SyncDirection) else connector.direction
175
+ direction_str = intended_direction.value if hasattr(intended_direction, "value") else str(intended_direction)
176
+
177
+ # Mark this entity's sync as in progress (and refuse to start a second concurrent
178
+ # run for the same entity). The UI polls these rows for live status.
179
+ run_id = await _begin_run(ctx.company_id, connector.name, entity, direction_str, started_at)
180
+ if run_id == _BUSY:
181
+ return SyncResult(
182
+ entity=entity,
183
+ direction=intended_direction,
184
+ errors=[f"{entity} sync already in progress"],
185
+ )
186
+
95
187
  # Incremental by default: pull only what changed since the last successful run
96
188
  # for this entity. Idempotency keys make any overlap dup-safe.
97
189
  if since is None and entity not in _OUTBOUND_ENTITIES:
98
190
  since = await _last_success_watermark(ctx.company_id, connector.name, entity)
99
191
 
100
- started_at = datetime.now(timezone.utc)
101
-
102
192
  try:
103
193
  if entity in _OUTBOUND_ENTITIES:
104
194
  result = await sync_method(ctx)
@@ -126,26 +216,7 @@ async def run_sync(
126
216
  else:
127
217
  status = "success"
128
218
 
129
- run = SyncRun(
130
- company_id=ctx.company_id,
131
- connector=connector.name,
132
- entity=entity,
133
- direction=result.direction.value if hasattr(result.direction, "value") else str(result.direction),
134
- started_at=started_at,
135
- finished_at=finished_at,
136
- created_count=result.created,
137
- updated_count=result.updated,
138
- skipped_count=result.skipped,
139
- errors_json=json.dumps(result.errors) if result.errors else None,
140
- status=status,
141
- )
142
-
143
- try:
144
- async with get_session_ctx() as session:
145
- session.add(run)
146
- await session.commit()
147
- except Exception as exc:
148
- log.warning("Failed to record SyncRun: %s", exc)
219
+ await _finish_run(run_id, ctx.company_id, connector.name, entity, result, started_at, finished_at, status)
149
220
 
150
221
  log.info(
151
222
  "sync_run %s.%s company=%s status=%s created=%d updated=%d skipped=%d errors=%d",
@@ -9,51 +9,53 @@ light and to respect default-module load order.
9
9
  from __future__ import annotations
10
10
 
11
11
 
12
- async def upsert_item(company_id: str, item) -> bool:
12
+ async def upsert_item(company_id: str, item) -> str:
13
13
  from celerp_inventory import services as items_svc
14
14
  return await items_svc.upsert_from_connector(company_id, item)
15
15
 
16
16
 
17
- async def upsert_order_from_shopify(company_id: str, order: dict) -> bool:
17
+ async def upsert_order_from_shopify(company_id: str, order: dict) -> str:
18
18
  from celerp_docs import doc_service as docs_svc
19
19
  return await docs_svc.upsert_order_from_shopify(company_id, order)
20
20
 
21
21
 
22
- async def upsert_invoice_from_quickbooks(company_id: str, invoice: dict) -> bool:
22
+ async def upsert_invoice_from_quickbooks(company_id: str, invoice: dict) -> str:
23
23
  from celerp_docs import doc_service as docs_svc
24
24
  return await docs_svc.upsert_invoice_from_quickbooks(company_id, invoice)
25
25
 
26
26
 
27
- async def upsert_invoice_from_xero(company_id: str, invoice: dict) -> bool:
27
+ async def upsert_invoice_from_xero(company_id: str, invoice: dict) -> str:
28
28
  from celerp_docs import doc_service as docs_svc
29
29
  return await docs_svc.upsert_invoice_from_xero(company_id, invoice)
30
30
 
31
31
 
32
- async def upsert_order_from_woocommerce(company_id: str, order: dict) -> bool:
32
+ async def upsert_order_from_woocommerce(company_id: str, order: dict) -> str:
33
33
  from celerp_docs import doc_service as docs_svc
34
34
  return await docs_svc.upsert_order_from_woocommerce(company_id, order)
35
35
 
36
36
 
37
- async def upsert_contact_from_shopify(company_id: str, customer: dict) -> bool:
37
+ async def upsert_contact_from_shopify(company_id: str, customer: dict) -> str:
38
38
  from celerp_contacts import services as contacts_svc
39
39
  return await contacts_svc.upsert_contact_from_shopify(company_id, customer)
40
40
 
41
41
 
42
- async def upsert_contact_from_quickbooks(company_id: str, customer: dict) -> bool:
42
+ async def upsert_contact_from_quickbooks(company_id: str, customer: dict) -> str:
43
43
  from celerp_contacts import services as contacts_svc
44
44
  return await contacts_svc.upsert_contact_from_quickbooks(company_id, customer)
45
45
 
46
46
 
47
- async def upsert_contact_from_xero(company_id: str, contact: dict) -> bool:
47
+ async def upsert_contact_from_xero(company_id: str, contact: dict) -> str:
48
48
  from celerp_contacts import services as contacts_svc
49
49
  return await contacts_svc.upsert_contact_from_xero(company_id, contact)
50
50
 
51
51
 
52
- async def upsert_contact_from_woocommerce(company_id: str, customer: dict) -> bool:
52
+ async def upsert_contact_from_woocommerce(company_id: str, customer: dict) -> str:
53
53
  from celerp_contacts import services as contacts_svc
54
54
  return await contacts_svc.upsert_contact_from_woocommerce(company_id, customer)
55
55
 
56
56
 
57
+ # -- Outbound: read the Celerp-side list of records to push ---------------------
58
+
57
59
  async def list_items_with_external_id(company_id: str, platform: str) -> list:
58
60
  from celerp_inventory import services as items_svc
59
61
  return await items_svc.list_items_with_external_id(company_id, platform=platform)
@@ -67,3 +69,8 @@ async def list_items_modified_since_last_sync(company_id: str, platform: str) ->
67
69
  async def list_unsynced_invoices(company_id: str, platform: str) -> list:
68
70
  from celerp_docs import doc_service as docs_svc
69
71
  return await docs_svc.list_unsynced_invoices(company_id, platform=platform)
72
+
73
+
74
+ async def mark_doc_pushed(company_id: str, entity_id: str, platform: str, external_id: str, entity: str = "invoice") -> None:
75
+ from celerp_docs import doc_service as docs_svc
76
+ return await docs_svc.mark_doc_pushed(company_id, entity_id, platform, external_id, entity=entity)