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.
- {celerp-1.2.6 → celerp-1.2.7}/CONTRIBUTING.md +4 -5
- {celerp-1.2.6 → celerp-1.2.7}/PKG-INFO +1 -1
- {celerp-1.2.6 → celerp-1.2.7}/celerp/cli.py +19 -9
- {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/quickbooks.py +9 -7
- {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/shopify.py +8 -18
- {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/sync_runner.py +31 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/upsert.py +22 -17
- celerp-1.2.7/celerp/connectors/util.py +19 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/webhooks.py +46 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/woocommerce.py +44 -29
- {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/xero.py +18 -13
- {celerp-1.2.6 → celerp-1.2.7}/celerp/gateway/client.py +19 -0
- celerp-1.2.7/celerp/migrations/_json_compat.py +84 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/n8o9p0q1r2s3_backfill_payment_date.py +33 -34
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/o9p0q1r2s3t4_backfill_item_price_fields.py +17 -17
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/u6v7w8x9y0z1_backfill_item_prices_from_attributes.py +16 -25
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/v7w8x9y0z1a2_backfill_purchase_unit_defaults.py +19 -17
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/w8x9y0z1a2b3_backfill_category_unit_defaults.py +32 -33
- {celerp-1.2.6 → celerp-1.2.7}/celerp.egg-info/PKG-INFO +1 -1
- {celerp-1.2.6 → celerp-1.2.7}/celerp.egg-info/SOURCES.txt +4 -1
- {celerp-1.2.6 → celerp-1.2.7}/celerp.egg-info/scm_file_list.json +10 -1
- celerp-1.2.7/celerp.egg-info/scm_version.json +8 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/conftest.py +1 -1
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_compliance.py +3 -2
- celerp-1.2.7/default_modules/celerp-connectors/tests/test_connector_correctness.py +226 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_connectors.py +2 -10
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_outbound.py +4 -4
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_quickbooks.py +4 -4
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_wiring.py +119 -1
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_woocommerce.py +51 -0
- celerp-1.2.7/default_modules/celerp-connectors/tests/test_woocommerce_webhooks.py +114 -0
- celerp-1.2.7/default_modules/celerp-contacts/celerp_contacts/services.py +181 -0
- celerp-1.2.7/default_modules/celerp-docs/celerp_docs/doc_service.py +316 -0
- celerp-1.2.7/default_modules/celerp-inventory/celerp_inventory/services.py +141 -0
- {celerp-1.2.6 → celerp-1.2.7}/pyproject.toml +4 -1
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_connectors.py +78 -2
- celerp-1.2.6/celerp/connectors/outbound_queue.py +0 -207
- celerp-1.2.6/celerp.egg-info/scm_version.json +0 -8
- celerp-1.2.6/default_modules/celerp-contacts/celerp_contacts/services.py +0 -84
- celerp-1.2.6/default_modules/celerp-docs/celerp_docs/doc_service.py +0 -80
- celerp-1.2.6/default_modules/celerp-inventory/celerp_inventory/services.py +0 -80
- {celerp-1.2.6 → celerp-1.2.7}/CLA.md +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/LICENSE +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/LICENSING.md +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/MANIFEST.in +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/NOTICE +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/README.md +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/alembic.ini +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/__main__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/batch.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/cleanup.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/commands.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/conversations.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/files.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/intent.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/llm.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/memory.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/models.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/page_count.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/quota.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/service.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/ai/tools.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/alembic.ini +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/alembic_config.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/compute/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/compute/aggregations.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/compute/valuation.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/config.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/base.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/daily_scheduler.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/http.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/images.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/registry.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/connectors/relay_token.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/constants.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/db.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/events/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/events/engine.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/events/schemas.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/events/types.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/gateway/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/gateway/state.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/importers/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/importers/importer.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/importers/schema.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/main.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/middleware.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/README.md +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/_auto_stamp.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/_data_reconcile.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/env.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/script.py.mako +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/a0b1c2d3e4f5_seed_landed_cost_clearing_accounts.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/a1b2c3d4e5f6_add_doc_share_tokens.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/b1c2d3e4f5a6_add_supporter_badges.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/b2c3d4e5f6a7_add_import_batches.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/c1d2e3f4a5b6_add_marketplace.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/d2e3f4a5b6c7_add_password_reset_token.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/e3f4a5b6c7d8_add_company_is_active.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/f1a2b3c4d5e6_move_accounts_marketplace_to_modules.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/fd5de461e14e_initial_schema.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/g1h2i3j4k5l6_add_consignment_flag.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/h2i3j4k5l6m7_add_bank_accounts_and_reconciliation.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/j4k5l6m7n8o9_labels_db_persistence.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/k5l6m7n8o9p0_add_reconciliation_v2.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/l6m7n8o9p0q1_add_sync_runs_table.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/m7n8o9p0q1r2_add_connector_configs_and_outbound_queue.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/p1q2r3s4t5u6_add_session_registry_and_user_auth_state.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/q2r3s4t5u6v7_add_system_runtime_state.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/r3s4t5u6v7w8_add_projections_company_entity_type_index.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/s4t5u6v7w8x9_drop_user_company_id_role.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/t5u6v7w8x9y0_seed_inventory_sub_accounts.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/x7y8z9a0b1c2_add_projection_created_at.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/y8z9a0b1c2d3_per_company_ledger_idempotency.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/migrations/versions/z9a0b1c2d3e4_work_centers.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/models/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/models/accounting.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/models/ai.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/models/auth.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/models/base.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/models/company.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/models/connector_config.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/models/import_batch.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/models/ledger.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/models/marketplace.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/models/notification.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/models/projections.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/models/share.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/models/supporter.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/models/sync_run.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/modules/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/modules/api.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/modules/audit.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/modules/license.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/modules/loader.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/modules/references.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/modules/registry.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/modules/slots.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/notifications/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/notifications/service.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/notifications/sse.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/output/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/output/pdf.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/output/share_render.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/projections/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/projections/engine.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/projections/handlers/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/projections/handlers/marketplace.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/projections/handlers/scanning.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/projections/handlers/system.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/auth.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/companies.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/debug.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/doctor.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/events.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/health.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/ledger.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/notifications.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/stars.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/routers/system.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/schemas/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/activity_redaction.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/attachments.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/auth.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/auto_je.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/backup.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/backup_export.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/backup_import.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/backup_repo.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/backup_scheduler.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/backup_state.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/demo.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/dev_release_guard.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/email.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/field_schema.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/fulfill.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/je_keys.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/landed_cost.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/line_measures.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/list_behavior.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/money.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/pick.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/runtime_state.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/session_tracker.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/star_cta.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/supporter_badge.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/system_health.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/services/units.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/session_gate.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp/tax_regimes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp.egg-info/dependency_links.txt +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp.egg-info/entry_points.txt +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp.egg-info/requires.txt +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/celerp.egg-info/top_level.txt +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/conftest.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/LICENSE +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/celerp_accounting/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/celerp_accounting/api_setup.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/celerp_accounting/csv_parser.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/celerp_accounting/matcher.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/celerp_accounting/models.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/celerp_accounting/projections.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/celerp_accounting/routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/celerp_accounting/ui_routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/tests/test_accounting_banking.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/tests/test_accounting_banking_routers.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/tests/test_accounting_gaps.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/tests/test_accounting_import_idempotency.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-accounting/tests/test_reconciliation_v2.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-admin/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-admin/celerp_admin/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-admin/celerp_admin/routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-admin/celerp_admin/setup.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/celerp_ai/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/celerp_ai/routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/celerp_ai/setup.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/celerp_ai/ui_routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/conftest.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_batch.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_cleanup.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_conversations.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_coverage_gaps.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_intent.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_llm.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_memory.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_models.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_page_count.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_quota.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_router.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_routes_coverage.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_service.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-ai/tests/test_tools.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-backup/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-backup/celerp_backup/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-backup/celerp_backup/routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-backup/celerp_backup/setup.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/celerp_connectors/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/celerp_connectors/models.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/celerp_connectors/routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_images.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_rate_limiting.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_shopify.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-connectors/tests/test_xero.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-contacts/LICENSE +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-contacts/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-contacts/celerp_contacts/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-contacts/celerp_contacts/migrations.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-contacts/celerp_contacts/projections.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-contacts/celerp_contacts/routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-contacts/celerp_contacts/ui_routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-dashboard/LICENSE +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-dashboard/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-dashboard/celerp_dashboard/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-dashboard/celerp_dashboard/routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-dashboard/celerp_dashboard/setup.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/LICENSE +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/api_setup.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/doc_constants.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/doc_projections.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/models_share.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/routes_share.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/sequences.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/taxes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/celerp_docs/ui_routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/conftest.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_audits.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_freight_je.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_freight_lines.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_fulfill_no_split.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_fulfillment_activity.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_inbound_je_lifecycle.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_inventory_account_reconciliation.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_landed_receive.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_line_delete_persist.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_payment_void_activity.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_send_visibility.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-docs/tests/test_service_lines.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/LICENSE +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/celerp_inventory/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/celerp_inventory/models_import_batch.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/celerp_inventory/projections.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/celerp_inventory/routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/celerp_inventory/routes_attachments.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/celerp_inventory/routes_scanning.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/celerp_inventory/ui_routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/tests/test_item_recipe_projection.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/tests/test_item_workflow_projection.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-inventory/tests/test_landed_cost_projection.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/LICENSE +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/celerp_labels/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/celerp_labels/migrations/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/celerp_labels/migrations/labels_001_create_label_templates.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/celerp_labels/models.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/celerp_labels/presets.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/celerp_labels/routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/celerp_labels/service.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/celerp_labels/ui_routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/requirements.txt +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/tests/test_labels.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-labels/tests/test_labels_module.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/LICENSE +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/celerp_manufacturing/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/celerp_manufacturing/costing.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/celerp_manufacturing/expansion.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/celerp_manufacturing/labor.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/celerp_manufacturing/migrations.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/celerp_manufacturing/projection_handler.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/celerp_manufacturing/routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/celerp_manufacturing/ui_routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/requirements.txt +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_bom_removed.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_build_api.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_item_recipe_api.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_item_workflow_api.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_labor_providers.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_manufacture_from_doc.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_manufacturing.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_manufacturing_import_idempotency.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_manufacturing_module.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_mfg_events.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_phase2_manufacturing_validations.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_production_order.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_recipe_costing.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_recipe_expansion.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_recost_api.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_to_make_allocation.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_where_used.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_work_centers_and_settings.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-manufacturing/tests/test_workflow_time.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-reports/LICENSE +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-reports/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-reports/celerp_reports/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-reports/celerp_reports/api_setup.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-reports/celerp_reports/routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-reports/celerp_reports/ui_routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-reports/tests/test_reports_full.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-reports/tests/test_reports_unit.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-subscriptions/LICENSE +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-subscriptions/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-subscriptions/celerp_subscriptions/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-subscriptions/celerp_subscriptions/routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-subscriptions/celerp_subscriptions/ui_routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-subscriptions/tests/test_subscriptions.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-subscriptions/tests/test_subscriptions_ui.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/LICENSE +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/accessory_fashion.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/activewear.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/art_photography.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/audio_equipment.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/bag_handbag.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/banknote.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/beauty_tool.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/bed_bedroom.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/beer.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/beverage_nonalc.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/body_part_auto.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/book.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/bottoms.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/brake_suspension.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/bullion_coin.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/camera.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/colored_stone.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/comic.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/commercial_space.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/component_part.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/confectionery.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/consulting_service.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/decorative_art.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/diamond.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/drawing.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/dress_jumpsuit.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/electrical_auto.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/electrical_component.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/emerald.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/engine_part.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/fastener.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/fertilizer_chemical.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/film_video.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/fine_writing_instrument.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/fluid_lubricant.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/footwear.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/fragrance.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/fresh_food.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/fresh_produce.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/frozen_food.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/gaming_console.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/gold_bullion.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/grain_cereal.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/haircare.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/hand_tool.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/ingredient_bulk.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/interior_part.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/jewelry.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/kitchen_dining.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/laptop.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/lighting.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/livestock_feed.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/makeup.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/measuring_instrument.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/medal_token.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/mineral_specimen.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/mobile_phone.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/music_cd.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/music_vinyl.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/nail.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/numismatic_coin.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/outdoor_furniture.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/outerwear.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/packaged_food.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/painting.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/parking_bay.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/pearl.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/personal_care.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/pipe_plumbing.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/platinum_bullion.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/power_tool.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/print_edition.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/residential_unit.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/rough_gemstone.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/ruby.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/saas_plan.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/safety_ppe.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/sapphire.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/sculpture.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/seating.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/seeds.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/silver_bullion.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/skincare.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/soft_furnishing.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/software_addon.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/software_license.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/spirit.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/storage_furniture.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/swimwear.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/table_desk.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/tablet.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/tire_wheel.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/tops.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/trading_card.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/tv_display.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/video_game.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/watch.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/watch_strap.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/categories/wine.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/agricultural.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/artwork.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/automotive.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/blank.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/books_media.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/coins_precious_metals.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/consulting.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/cosmetics.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/electronics.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/fashion.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/food_beverage.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/furniture.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/gemstones.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/hardware.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/property_rental.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/saas.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/watches.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/presets/wine_spirits.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/celerp_verticals/ui_routes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/tests/test_verticals.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/default_modules/celerp-verticals/tests/test_verticals_routers.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/legal/OFFICIAL-COMPONENTS.md +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/legal/TRADEMARK.md +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/memory/fix-receive-goods-bugs.md +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/setup.cfg +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/test_helpers.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/api_client.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/app.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/components/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/components/activity.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/components/backup.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/components/cloud_gate.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/components/currency.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/components/files.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/components/notes.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/components/phone.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/components/shell.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/components/table.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/config.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/i18n.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/locales/ar.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/locales/de.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/locales/en.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/locales/es.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/locales/fr.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/locales/id.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/locales/it.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/locales/ja.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/locales/pt.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/locales/th.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/locales/vi.json +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/__init__.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/accounting.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/accounting_import.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/auth.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/contacts.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/csv_import.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/dashboard.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/docs_import.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/documents.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/events.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/inventory.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/lists_import.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/manufacturing.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/notifications.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/reconciliation.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/reports.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/search.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_accounting.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_cloud.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_contacts.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_general.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_import.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_inventory.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_manufacturing.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_purchasing.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/settings_sales.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/setup.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/stars.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/subscriptions.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/routes/subscriptions_import.py +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/static/app.css +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/static/htmx.min.js +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/static/icon.png +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/static/logo.png +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/static/vendor/intl-tel-input/img/flags.webp +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/static/vendor/intl-tel-input/img/flags@2x.webp +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/static/vendor/intl-tel-input/intlTelInput.min.css +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/static/vendor/intl-tel-input/intlTelInput.min.js +0 -0
- {celerp-1.2.6 → celerp-1.2.7}/ui/static/vendor/intl-tel-input/intlTelInputWithUtils.min.js +0 -0
- {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
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
|
@@ -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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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=
|
|
157
|
-
cost_price=
|
|
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=
|
|
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
|
|
180
|
-
#
|
|
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"].
|
|
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
|
|
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
|
|
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
|
|
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
|
|
29
|
-
return await
|
|
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
|
|
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
|
-
|
|
92
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
sell_price =
|
|
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
|
-
|
|
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
|
-
|
|
185
|
-
|
|
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(
|
|
403
|
-
|
|
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
|
-
|
|
411
|
-
f"{
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
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
|
-
|
|
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(
|
|
68
|
-
|
|
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
|
-
|
|
77
|
-
|
|
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=
|
|
131
|
-
cost_price=
|
|
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
|
|