celerp 2.0.0__tar.gz → 2.1.0__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-2.0.0 → celerp-2.1.0}/PKG-INFO +1 -1
- {celerp-2.0.0 → celerp-2.1.0}/celerp/cli.py +51 -22
- {celerp-2.0.0 → celerp-2.1.0}/celerp/events/schemas.py +7 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/main.py +28 -2
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/_data_reconcile.py +30 -12
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/env.py +6 -1
- celerp-2.1.0/celerp/migrations/versions/e7c9a1b3d5f2_work_center_default_columns.py +51 -0
- celerp-2.1.0/celerp/migrations/versions/f8a9b0c1d2e3_work_center_default_backfill.py +93 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/models/company.py +11 -1
- {celerp-2.0.0 → celerp-2.1.0}/celerp/modules/slots.py +4 -2
- {celerp-2.0.0 → celerp-2.1.0}/celerp/routers/companies.py +16 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/backup_import.py +13 -4
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/field_schema.py +6 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/permissions.py +30 -29
- celerp-2.1.0/celerp/services/reorder.py +372 -0
- celerp-2.1.0/celerp/services/status_doc_backfill.py +101 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp.egg-info/PKG-INFO +1 -1
- {celerp-2.0.0 → celerp-2.1.0}/celerp.egg-info/SOURCES.txt +5 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp.egg-info/scm_file_list.json +958 -945
- celerp-2.1.0/celerp.egg-info/scm_version.json +8 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-inventory/celerp_inventory/routes.py +123 -11
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/__init__.py +6 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/celerp_manufacturing/routes.py +189 -15
- celerp-2.1.0/default_modules/celerp-manufacturing/tests/test_auto_complete_on_finalize.py +171 -0
- celerp-2.1.0/default_modules/celerp-manufacturing/tests/test_work_centers_and_settings.py +374 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/first_party.lock.json +2 -2
- {celerp-2.0.0 → celerp-2.1.0}/ui/api_client.py +5 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/components/activity.py +34 -1
- {celerp-2.0.0 → celerp-2.1.0}/ui/components/cloud_gate.py +37 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/components/shell.py +217 -6
- {celerp-2.0.0 → celerp-2.1.0}/ui/components/table.py +19 -6
- {celerp-2.0.0 → celerp-2.1.0}/ui/locales/ar.json +4 -1
- {celerp-2.0.0 → celerp-2.1.0}/ui/locales/de.json +4 -1
- {celerp-2.0.0 → celerp-2.1.0}/ui/locales/en.json +12 -1
- {celerp-2.0.0 → celerp-2.1.0}/ui/locales/es.json +4 -1
- {celerp-2.0.0 → celerp-2.1.0}/ui/locales/fr.json +4 -1
- {celerp-2.0.0 → celerp-2.1.0}/ui/locales/id.json +4 -1
- {celerp-2.0.0 → celerp-2.1.0}/ui/locales/it.json +4 -1
- {celerp-2.0.0 → celerp-2.1.0}/ui/locales/ja.json +4 -1
- {celerp-2.0.0 → celerp-2.1.0}/ui/locales/pt.json +4 -1
- {celerp-2.0.0 → celerp-2.1.0}/ui/locales/th.json +4 -1
- {celerp-2.0.0 → celerp-2.1.0}/ui/locales/vi.json +4 -1
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/account.py +33 -6
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/contacts.py +3 -2
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/documents.py +8 -6
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/inventory.py +880 -446
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/manufacturing.py +74 -17
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/settings.py +6 -6
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/settings_accounting.py +1 -1
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/settings_connectors.py +2 -14
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/settings_inventory.py +33 -9
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/settings_manufacturing.py +31 -26
- celerp-2.1.0/ui/security.py +19 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/static/app.css +63 -14
- celerp-2.0.0/celerp/services/reorder.py +0 -279
- celerp-2.0.0/celerp.egg-info/scm_version.json +0 -8
- celerp-2.0.0/default_modules/celerp-manufacturing/tests/test_work_centers_and_settings.py +0 -120
- {celerp-2.0.0 → celerp-2.1.0}/CLA.md +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/CONTRIBUTING.md +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/LICENSING.md +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/MANIFEST.in +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/NOTICE +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/README.md +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/alembic.ini +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/__main__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/ai/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/ai/batch.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/ai/cleanup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/ai/commands.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/ai/conversations.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/ai/files.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/ai/intent.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/ai/llm.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/ai/memory.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/ai/models.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/ai/page_count.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/ai/quota.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/ai/service.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/ai/tools.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/alembic.ini +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/alembic_config.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/compat.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/compute/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/compute/aggregations.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/compute/valuation.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/config.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/connectors/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/connectors/base.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/connectors/daily_scheduler.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/connectors/http.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/connectors/images.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/connectors/quickbooks.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/connectors/registry.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/connectors/relay_token.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/connectors/shopify.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/connectors/sync_runner.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/connectors/upsert.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/connectors/util.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/connectors/webhooks.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/connectors/woocommerce.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/connectors/xero.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/constants.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/db.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/embedded_pg.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/events/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/events/engine.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/events/types.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/gateway/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/gateway/client.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/gateway/state.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/importers/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/importers/importer.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/importers/schema.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/middleware.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/README.md +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/_auto_stamp.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/_json_compat.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/script.py.mako +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/a0b1c2d3e4f5_seed_landed_cost_clearing_accounts.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/a1b2c3d4e5f6_add_doc_share_tokens.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/b1c2d3e4f5a6_add_supporter_badges.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/b2c3d4e5f6a7_add_import_batches.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/c1d2e3f4a5b6_add_marketplace.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/c2d3e4f5a6b7_add_is_sync_to_shopify.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/c4d5e6f7a8b9_account_cash_flow_category.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/d0e1f2a3b4c5_add_share_token_expiry.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/d2e3f4a5b6c7_add_password_reset_token.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/d5e6f7a8b9c0_rename_fx_rounding_to_fx_difference.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/e3f4a5b6c7d8_add_company_is_active.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/e4f5a6b7c8d9_backfill_connector_idempotency_key.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/f1a2b3c4d5e6_move_accounts_marketplace_to_modules.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/f6a7b8c9d0e1_seed_fx_rounding_account.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/fd5de461e14e_initial_schema.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/g1h2i3j4k5l6_add_consignment_flag.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/h2i3j4k5l6m7_add_bank_accounts_and_reconciliation.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/j4k5l6m7n8o9_labels_db_persistence.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/k5l6m7n8o9p0_add_reconciliation_v2.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/l6m7n8o9p0q1_add_sync_runs_table.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/m7n8o9p0q1r2_add_connector_configs_and_outbound_queue.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/n8o9p0q1r2s3_backfill_payment_date.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/o9p0q1r2s3t4_backfill_item_price_fields.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/p1q2r3s4t5u6_add_session_registry_and_user_auth_state.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/q2r3s4t5u6v7_add_system_runtime_state.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/r3s4t5u6v7w8_add_projections_company_entity_type_index.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/s4t5u6v7w8x9_drop_user_company_id_role.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/t5u6v7w8x9y0_seed_inventory_sub_accounts.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/u6v7w8x9y0z1_backfill_item_prices_from_attributes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/v7w8x9y0z1a2_backfill_purchase_unit_defaults.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/w8x9y0z1a2b3_backfill_category_unit_defaults.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/x7y8z9a0b1c2_add_projection_created_at.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/y8z9a0b1c2d3_per_company_ledger_idempotency.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/migrations/versions/z9a0b1c2d3e4_work_centers.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/models/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/models/accounting.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/models/ai.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/models/auth.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/models/base.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/models/connector_config.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/models/import_batch.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/models/ledger.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/models/marketplace.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/models/notification.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/models/projections.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/models/share.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/models/supporter.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/models/sync_run.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/modules/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/modules/api.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/modules/audit.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/modules/demotion.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/modules/importer.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/modules/license.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/modules/loader.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/modules/meta.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/modules/migrations_runner.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/modules/references.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/modules/registry.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/notifications/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/notifications/service.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/notifications/sse.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/output/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/output/branding.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/output/doc_print.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/output/pdf.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/output/share_render.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/projections/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/projections/engine.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/projections/handlers/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/projections/handlers/marketplace.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/projections/handlers/scanning.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/projections/handlers/shopify.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/projections/handlers/system.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/routers/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/routers/auth.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/routers/debug.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/routers/doctor.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/routers/events.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/routers/health.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/routers/ledger.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/routers/notifications.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/routers/stars.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/routers/system.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/schemas/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/activity_redaction.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/attachments.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/auth.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/auto_je.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/backup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/backup_export.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/backup_repo.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/backup_scheduler.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/backup_state.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/cost_visibility.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/demo.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/dev_release_guard.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/email.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/fulfill.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/holdings.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/je_keys.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/landed_cost.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/line_measures.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/list_behavior.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/money.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/payments.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/pick.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/pricing.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/relay_share.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/runtime_state.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/session_tracker.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/shipping.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/star_cta.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/supporter_badge.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/system_health.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/services/units.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/session_gate.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp/tax_regimes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp.egg-info/dependency_links.txt +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp.egg-info/entry_points.txt +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp.egg-info/requires.txt +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/celerp.egg-info/top_level.txt +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/conftest.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/celerp_accounting/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/celerp_accounting/api_setup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/celerp_accounting/csv_parser.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/celerp_accounting/matcher.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/celerp_accounting/models.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/celerp_accounting/projections.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/celerp_accounting/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/celerp_accounting/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/report_goldens/balance-sheet.html +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/report_goldens/cash-flow.html +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/report_goldens/extended-journal-csv.csv +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/report_goldens/extended-journal-print.html +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/report_goldens/extended-journal.html +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/report_goldens/general-ledger.html +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/report_goldens/journal-csv.csv +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/report_goldens/journal-print.html +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/report_goldens/journal.html +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/report_goldens/ledger.html +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/report_goldens/pnl.html +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/report_goldens/statement.html +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/report_goldens/trial-balance.html +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/test_accounting_banking.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/test_accounting_banking_routers.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/test_accounting_gaps.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/test_accounting_import_idempotency.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/test_cash_flow.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/test_financial_reports_ui.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/test_journal_gl_soa.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/test_journal_gl_soa_ui.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/test_reconciliation_v2.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/test_reports_end_to_end.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-accounting/tests/test_route_permissions.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-admin/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-admin/celerp_admin/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-admin/celerp_admin/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-admin/celerp_admin/setup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/celerp_ai/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/celerp_ai/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/celerp_ai/setup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/celerp_ai/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/tests/conftest.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/tests/test_batch.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/tests/test_cleanup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/tests/test_conversations.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/tests/test_coverage_gaps.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/tests/test_intent.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/tests/test_llm.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/tests/test_memory.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/tests/test_models.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/tests/test_page_count.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/tests/test_quota.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/tests/test_router.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/tests/test_routes_coverage.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/tests/test_service.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-ai/tests/test_tools.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-backup/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-backup/celerp_backup/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-backup/celerp_backup/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-backup/celerp_backup/setup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/celerp_connectors/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/celerp_connectors/models.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/celerp_connectors/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/tests/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/tests/conftest.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/tests/test_compliance.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/tests/test_connector_correctness.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/tests/test_connectors.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/tests/test_credentials_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/tests/test_images.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/tests/test_outbound.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/tests/test_quickbooks.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/tests/test_rate_limiting.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/tests/test_shopify.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/tests/test_wiring.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/tests/test_woocommerce.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/tests/test_woocommerce_webhooks.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-connectors/tests/test_xero.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-contacts/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-contacts/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-contacts/celerp_contacts/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-contacts/celerp_contacts/migrations.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-contacts/celerp_contacts/projections.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-contacts/celerp_contacts/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-contacts/celerp_contacts/services.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-contacts/celerp_contacts/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-dashboard/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-dashboard/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-dashboard/celerp_dashboard/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-dashboard/celerp_dashboard/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-dashboard/celerp_dashboard/setup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/celerp_docs/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/celerp_docs/api_setup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/celerp_docs/doc_constants.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/celerp_docs/doc_email.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/celerp_docs/doc_projections.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/celerp_docs/doc_service.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/celerp_docs/models_share.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/celerp_docs/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/celerp_docs/routes_payments.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/celerp_docs/routes_share.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/celerp_docs/sequences.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/celerp_docs/taxes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/celerp_docs/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/tests/conftest.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/tests/test_audits.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/tests/test_freight_je.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/tests/test_freight_lines.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/tests/test_fulfill_no_split.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/tests/test_fulfillment_activity.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/tests/test_inbound_je_lifecycle.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/tests/test_inventory_account_reconciliation.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/tests/test_landed_receive.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/tests/test_line_delete_persist.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/tests/test_line_identifier.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/tests/test_payment_void_activity.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/tests/test_receive_sku_batch.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/tests/test_send_visibility.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/tests/test_service_lines.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-docs/tests/test_void_guards.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-inventory/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-inventory/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-inventory/celerp_inventory/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-inventory/celerp_inventory/models_import_batch.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-inventory/celerp_inventory/projections.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-inventory/celerp_inventory/routes_attachments.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-inventory/celerp_inventory/routes_scanning.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-inventory/celerp_inventory/services.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-inventory/celerp_inventory/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-inventory/tests/test_item_recipe_projection.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-inventory/tests/test_item_workflow_projection.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-inventory/tests/test_landed_cost_projection.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-labels/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-labels/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-labels/celerp_labels/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-labels/celerp_labels/models.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-labels/celerp_labels/presets.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-labels/celerp_labels/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-labels/celerp_labels/service.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-labels/celerp_labels/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-labels/requirements.txt +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-labels/tests/test_labels.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-labels/tests/test_labels_module.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/celerp_manufacturing/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/celerp_manufacturing/costing.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/celerp_manufacturing/expansion.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/celerp_manufacturing/labor.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/celerp_manufacturing/migrations.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/celerp_manufacturing/projection_handler.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/celerp_manufacturing/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/requirements.txt +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/tests/test_bom_removed.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/tests/test_build_api.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/tests/test_item_recipe_api.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/tests/test_item_workflow_api.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/tests/test_labor_providers.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/tests/test_manufacture_from_doc.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/tests/test_manufacturing.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/tests/test_manufacturing_import_idempotency.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/tests/test_manufacturing_module.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/tests/test_mfg_events.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/tests/test_phase2_manufacturing_validations.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/tests/test_production_order.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/tests/test_recipe_costing.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/tests/test_recipe_expansion.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/tests/test_recost_api.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/tests/test_to_make_allocation.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/tests/test_where_used.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-manufacturing/tests/test_workflow_time.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-reports/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-reports/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-reports/celerp_reports/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-reports/celerp_reports/api_setup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-reports/celerp_reports/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-reports/celerp_reports/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-reports/tests/test_reports_full.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-reports/tests/test_reports_unit.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-subscriptions/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-subscriptions/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-subscriptions/celerp_subscriptions/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-subscriptions/celerp_subscriptions/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-subscriptions/celerp_subscriptions/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-subscriptions/tests/test_subscriptions.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-subscriptions/tests/test_subscriptions_ui.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/accessory_fashion.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/activewear.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/art_photography.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/audio_equipment.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/bag_handbag.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/banknote.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/beauty_tool.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/bed_bedroom.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/beer.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/beverage_nonalc.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/body_part_auto.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/book.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/bottoms.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/brake_suspension.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/bullion_coin.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/camera.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/colored_stone.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/comic.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/commercial_space.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/component_part.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/confectionery.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/consulting_service.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/decorative_art.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/diamond.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/drawing.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/dress_jumpsuit.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/electrical_auto.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/electrical_component.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/emerald.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/engine_part.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/fastener.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/fertilizer_chemical.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/film_video.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/fine_writing_instrument.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/fluid_lubricant.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/footwear.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/fragrance.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/fresh_food.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/fresh_produce.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/frozen_food.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/gaming_console.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/gold_bullion.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/grain_cereal.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/haircare.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/hand_tool.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/ingredient_bulk.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/interior_part.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/jewelry.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/kitchen_dining.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/laptop.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/lighting.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/livestock_feed.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/makeup.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/measuring_instrument.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/medal_token.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/mineral_specimen.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/mobile_phone.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/music_cd.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/music_vinyl.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/nail.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/numismatic_coin.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/outdoor_furniture.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/outerwear.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/packaged_food.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/painting.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/parking_bay.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/pearl.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/personal_care.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/pipe_plumbing.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/platinum_bullion.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/power_tool.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/print_edition.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/residential_unit.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/rough_gemstone.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/ruby.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/saas_plan.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/safety_ppe.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/sapphire.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/sculpture.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/seating.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/seeds.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/silver_bullion.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/skincare.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/soft_furnishing.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/software_addon.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/software_license.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/spirit.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/storage_furniture.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/swimwear.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/table_desk.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/tablet.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/tire_wheel.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/tops.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/trading_card.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/tv_display.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/video_game.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/watch.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/watch_strap.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/categories/wine.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/presets/agricultural.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/presets/artwork.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/presets/automotive.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/presets/blank.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/presets/books_media.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/presets/coins_precious_metals.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/presets/consulting.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/presets/cosmetics.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/presets/electronics.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/presets/fashion.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/presets/food_beverage.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/presets/furniture.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/presets/gemstones.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/presets/hardware.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/presets/property_rental.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/presets/saas.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/presets/watches.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/presets/wine_spirits.json +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/celerp_verticals/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/tests/test_verticals.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/default_modules/celerp-verticals/tests/test_verticals_routers.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/legal/OFFICIAL-COMPONENTS.md +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/legal/TRADEMARK.md +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/packaging/winget/Celerp.Celerp.installer.yaml +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/packaging/winget/Celerp.Celerp.locale.en-US.yaml +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/packaging/winget/Celerp.Celerp.yaml +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/packaging/winget/README.md +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/pyproject.toml +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/setup.cfg +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/test_helpers.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/app.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/components/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/components/attrs.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/components/backup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/components/currency.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/components/files.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/components/journal.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/components/notes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/components/phone.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/components/report_kit.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/config.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/i18n.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/marketplace_catalog.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/accounting.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/accounting_import.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/auth.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/csv_import.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/dashboard.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/docs_import.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/events.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/financial_reports.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/lists_import.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/modules_page.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/notifications.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/reconciliation.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/reports.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/search.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/settings_cloud.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/settings_contacts.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/settings_general.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/settings_import.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/settings_payments.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/settings_purchasing.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/settings_sales.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/setup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/stars.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/subscriptions.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/routes/subscriptions_import.py +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/static/htmx.min.js +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/static/icon.png +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/static/logo.png +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/static/vendor/intl-tel-input/img/flags.webp +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/static/vendor/intl-tel-input/img/flags@2x.webp +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/static/vendor/intl-tel-input/intlTelInput.min.css +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/static/vendor/intl-tel-input/intlTelInput.min.js +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/static/vendor/intl-tel-input/intlTelInputWithUtils.min.js +0 -0
- {celerp-2.0.0 → celerp-2.1.0}/ui/static/vendor/intl-tel-input/utils.js +0 -0
|
@@ -10,6 +10,7 @@ import signal
|
|
|
10
10
|
import subprocess
|
|
11
11
|
import sys
|
|
12
12
|
import time
|
|
13
|
+
from contextlib import contextmanager
|
|
13
14
|
from pathlib import Path
|
|
14
15
|
|
|
15
16
|
import click
|
|
@@ -639,21 +640,19 @@ def _stamped_revision(db_url: str) -> str | None:
|
|
|
639
640
|
engine.dispose()
|
|
640
641
|
|
|
641
642
|
|
|
642
|
-
|
|
643
|
-
|
|
643
|
+
@contextmanager
|
|
644
|
+
def _migration_lock(db_url: str):
|
|
645
|
+
"""Hold the shared Postgres migration advisory lock for the wrapped block.
|
|
644
646
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
+
Single source of the lock protocol (acquire, guaranteed release, engine
|
|
648
|
+
disposal), so every path that migrates a database serializes the same way
|
|
649
|
+
and cannot drift. Reused by `_migrate_to_head` and the restore reconcile.
|
|
647
650
|
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
+
The lock exists because a service restart overlapping a manual start would
|
|
652
|
+
otherwise have both processes migrating the same database: the loser
|
|
653
|
+
re-applies DDL that already exists and is stamped past it by the
|
|
651
654
|
duplicate-object handler, which reaches the right answer for the wrong
|
|
652
655
|
reason. The second holder waits here and then finds nothing pending.
|
|
653
|
-
|
|
654
|
-
Reports the stamp it moved, and says nothing when it moved nothing, so a
|
|
655
|
-
routine start is as quiet as it was and a start that changed the schema
|
|
656
|
-
cannot be mistaken for one that did not.
|
|
657
656
|
"""
|
|
658
657
|
from sqlalchemy import create_engine, text
|
|
659
658
|
|
|
@@ -662,25 +661,40 @@ def _migrate_to_head(db_url: str) -> None:
|
|
|
662
661
|
engine = create_engine(_sync_url(db_url), pool_pre_ping=True).execution_options(
|
|
663
662
|
isolation_level="AUTOCOMMIT"
|
|
664
663
|
)
|
|
665
|
-
before = after = None
|
|
666
664
|
try:
|
|
667
665
|
with engine.connect() as lock_conn:
|
|
668
666
|
lock_conn.execute(text("SELECT pg_advisory_lock(:key)"), {"key": _MIGRATION_LOCK_KEY})
|
|
669
667
|
try:
|
|
670
|
-
|
|
671
|
-
_run_migrations(db_url)
|
|
672
|
-
_post_migration_grants(db_url)
|
|
673
|
-
_reconcile_after_migrate(db_url)
|
|
674
|
-
# Read inside the lock: outside it, a process queued behind this
|
|
675
|
-
# one could move the stamp further and this would report a
|
|
676
|
-
# transition that never happened here.
|
|
677
|
-
after = _stamped_revision(db_url)
|
|
668
|
+
yield
|
|
678
669
|
finally:
|
|
679
670
|
lock_conn.execute(
|
|
680
671
|
text("SELECT pg_advisory_unlock(:key)"), {"key": _MIGRATION_LOCK_KEY}
|
|
681
672
|
)
|
|
682
673
|
finally:
|
|
683
674
|
engine.dispose()
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
def _migrate_to_head(db_url: str) -> None:
|
|
678
|
+
"""Apply pending migrations, the grants, then the develop→release reconcile.
|
|
679
|
+
|
|
680
|
+
Shared by `celerp migrate` and `celerp start` so the steps and their order
|
|
681
|
+
exist once. The start path cannot drift from the explicit command. Held
|
|
682
|
+
under the shared migration advisory lock (`_migration_lock`).
|
|
683
|
+
|
|
684
|
+
Reports the stamp it moved, and says nothing when it moved nothing, so a
|
|
685
|
+
routine start is as quiet as it was and a start that changed the schema
|
|
686
|
+
cannot be mistaken for one that did not.
|
|
687
|
+
"""
|
|
688
|
+
before = after = None
|
|
689
|
+
with _migration_lock(db_url):
|
|
690
|
+
before = _stamped_revision(db_url)
|
|
691
|
+
_run_migrations(db_url)
|
|
692
|
+
_post_migration_grants(db_url)
|
|
693
|
+
_reconcile_after_migrate(db_url)
|
|
694
|
+
# Read inside the lock: outside it, a process queued behind this one
|
|
695
|
+
# could move the stamp further and this would report a transition that
|
|
696
|
+
# never happened here.
|
|
697
|
+
after = _stamped_revision(db_url)
|
|
684
698
|
if after != before:
|
|
685
699
|
click.echo(f" ✓ Database migrated: {before or 'base'} -> {after}")
|
|
686
700
|
|
|
@@ -693,6 +707,13 @@ def _reconcile_after_migrate(db_url: str) -> None:
|
|
|
693
707
|
are idempotent, so a redundant run would be a no-op anyway. The projection
|
|
694
708
|
rebuild (the other half of the version-change reconcile) runs in the API
|
|
695
709
|
lifespan, where module projection handlers are loaded.
|
|
710
|
+
|
|
711
|
+
Each backfill replays in its own transaction; the version marker is written
|
|
712
|
+
in a SEPARATE final transaction, and only when every backfill succeeded, so
|
|
713
|
+
a backfill that fails is retried on the next start instead of being marked
|
|
714
|
+
done for this version's lifetime. The marker write no longer shares a
|
|
715
|
+
transaction with the replay, so its own failure cannot roll back committed
|
|
716
|
+
backfills.
|
|
696
717
|
"""
|
|
697
718
|
import sqlalchemy as _sa
|
|
698
719
|
|
|
@@ -710,8 +731,16 @@ def _reconcile_after_migrate(db_url: str) -> None:
|
|
|
710
731
|
with engine.begin() as conn:
|
|
711
732
|
if get_meta(conn, BACKFILL_VERSION_KEY) == __version__:
|
|
712
733
|
return # already reconciled for this version
|
|
713
|
-
|
|
714
|
-
|
|
734
|
+
replayed, failures = replay_data_backfills(engine)
|
|
735
|
+
if failures:
|
|
736
|
+
click.echo(
|
|
737
|
+
f" ! {len(failures)} data-backfill migration(s) failed to reconcile for "
|
|
738
|
+
f"{__version__}; leaving the version marker unset so they retry next start: "
|
|
739
|
+
f"{', '.join(failures)}"
|
|
740
|
+
)
|
|
741
|
+
else:
|
|
742
|
+
with engine.begin() as conn:
|
|
743
|
+
set_meta(conn, BACKFILL_VERSION_KEY, __version__)
|
|
715
744
|
if replayed:
|
|
716
745
|
click.echo(f" · Reconciled {len(replayed)} data-backfill migration(s) for {__version__}")
|
|
717
746
|
finally:
|
|
@@ -125,6 +125,10 @@ class ItemSplit(BaseModel):
|
|
|
125
125
|
# One entry per child: {child_id, child_sku, origin_event_id, qty_before, qty_after,
|
|
126
126
|
# pieces_before, pieces_after, weight_before, weight_after}. Sequential mother deltas.
|
|
127
127
|
children_detail: list[dict[str, Any]] = Field(default_factory=list)
|
|
128
|
+
# Weight expected to remain minus weight measured, recorded only when a real
|
|
129
|
+
# measurement exists (full weighed break-up, or an operator re-weigh). None when
|
|
130
|
+
# the remainder is only derived, so no false anomaly is asserted.
|
|
131
|
+
delta: float | None = None
|
|
128
132
|
|
|
129
133
|
|
|
130
134
|
class ItemSplitFrom(BaseModel):
|
|
@@ -150,6 +154,9 @@ class ItemTransform(BaseModel):
|
|
|
150
154
|
pieces_after: float | None = None
|
|
151
155
|
weight_before: float | None = None
|
|
152
156
|
weight_after: float | None = None
|
|
157
|
+
# Processing/trim loss: mother weight in minus child weight out, recorded only
|
|
158
|
+
# when both weights exist. None otherwise (no fabricated loss).
|
|
159
|
+
delta: float | None = None
|
|
153
160
|
|
|
154
161
|
|
|
155
162
|
class ItemTransformedFrom(BaseModel):
|
|
@@ -211,9 +211,20 @@ async def lifespan(_app: FastAPI):
|
|
|
211
211
|
# instance that already has companies).
|
|
212
212
|
from celerp.modules.slots import fire_lifecycle as _fire
|
|
213
213
|
from celerp.db import SessionLocal as _SessionLocal
|
|
214
|
+
# Best-effort, like the two sibling blocks below: a hook that fails
|
|
215
|
+
# during flush poisons the shared session, so the commit raises.
|
|
216
|
+
# Roll back and log at ERROR rather than let that crash boot - the
|
|
217
|
+
# manufacturing seed hook, for one, must never be able to take the
|
|
218
|
+
# app down.
|
|
214
219
|
async with _SessionLocal() as _sess:
|
|
215
|
-
|
|
216
|
-
|
|
220
|
+
try:
|
|
221
|
+
await _fire("on_modules_ready", session=_sess)
|
|
222
|
+
await _sess.commit()
|
|
223
|
+
except Exception:
|
|
224
|
+
await _sess.rollback()
|
|
225
|
+
logging.getLogger(__name__).exception(
|
|
226
|
+
"on_modules_ready hooks failed (non-fatal); their data was rolled back"
|
|
227
|
+
)
|
|
217
228
|
|
|
218
229
|
# A bundled default whose content no longer matches the first-party
|
|
219
230
|
# lock is demoted to untrusted. Surface that in the notification bell
|
|
@@ -255,6 +266,21 @@ async def lifespan(_app: FastAPI):
|
|
|
255
266
|
"stale until rebuilt via doctor or /ledger/rebuild"
|
|
256
267
|
)
|
|
257
268
|
|
|
269
|
+
# One-time backfill: stamp the status→document pairing on items sold, memo'd,
|
|
270
|
+
# or consigned in before that field shipped, so their inventory status links
|
|
271
|
+
# to its document. Marker-gated (runs once); non-fatal like the guard above.
|
|
272
|
+
try:
|
|
273
|
+
from celerp.db import SessionLocal as _BackfillSession
|
|
274
|
+
from celerp.services.status_doc_backfill import run_status_doc_backfill
|
|
275
|
+
async with _BackfillSession() as _bf_sess:
|
|
276
|
+
await run_status_doc_backfill(_bf_sess)
|
|
277
|
+
await _bf_sess.commit()
|
|
278
|
+
except Exception:
|
|
279
|
+
logging.getLogger(__name__).exception(
|
|
280
|
+
"Status-doc backfill failed (non-fatal); pre-existing sold/memo items "
|
|
281
|
+
"may show their status without a document link until a later boot"
|
|
282
|
+
)
|
|
283
|
+
|
|
258
284
|
# Bring up the relay tunnel per the lazy free-tier lifecycle (3.1). A token-holder
|
|
259
285
|
# is past first activation and never re-enters it. Paid instances (public_url set)
|
|
260
286
|
# keep the tunnel always-on; a free instance opens it at boot only when it already
|
|
@@ -20,13 +20,16 @@ against — so no SQL is duplicated and nothing is monkeypatched.
|
|
|
20
20
|
"""
|
|
21
21
|
from __future__ import annotations
|
|
22
22
|
|
|
23
|
+
import logging
|
|
23
24
|
from pathlib import Path
|
|
24
25
|
from typing import TYPE_CHECKING
|
|
25
26
|
|
|
26
27
|
from celerp.migrations._auto_stamp import extract_signatures
|
|
27
28
|
|
|
28
29
|
if TYPE_CHECKING:
|
|
29
|
-
from sqlalchemy.engine import Connection
|
|
30
|
+
from sqlalchemy.engine import Connection, Engine
|
|
31
|
+
|
|
32
|
+
_log = logging.getLogger(__name__)
|
|
30
33
|
|
|
31
34
|
# ── Instance metadata marker ────────────────────────────────────────────────
|
|
32
35
|
# A tiny key/value table recording which celerp version last applied each
|
|
@@ -92,22 +95,37 @@ def data_backfill_scripts():
|
|
|
92
95
|
return out
|
|
93
96
|
|
|
94
97
|
|
|
95
|
-
def replay_data_backfills(
|
|
96
|
-
"""Re-run every data-backfill migration's `upgrade()
|
|
98
|
+
def replay_data_backfills(engine: "Engine") -> tuple[list[str], list[str]]:
|
|
99
|
+
"""Re-run every data-backfill migration's `upgrade()`, each in its OWN
|
|
100
|
+
transaction, against `engine`.
|
|
101
|
+
|
|
102
|
+
A per-backfill top-level transaction (not a savepoint inside one outer
|
|
103
|
+
transaction) is what makes the replay durable: each backfill that succeeds
|
|
104
|
+
commits on its own, so a later backfill failure, or a failed marker write
|
|
105
|
+
afterwards, cannot silently undo work that already converged. One failing
|
|
106
|
+
backfill is logged loudly and skipped; the rest still run.
|
|
97
107
|
|
|
98
|
-
`
|
|
99
|
-
|
|
108
|
+
Returns `(replayed, failures)`: the revision ids that applied cleanly and
|
|
109
|
+
the ids that raised, both in apply order.
|
|
100
110
|
"""
|
|
101
111
|
from alembic.operations import Operations
|
|
102
112
|
from alembic.runtime.migration import MigrationContext
|
|
103
113
|
|
|
104
114
|
scripts = data_backfill_scripts()
|
|
105
|
-
ctx = MigrationContext.configure(connection)
|
|
106
115
|
replayed: list[str] = []
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
116
|
+
failures: list[str] = []
|
|
117
|
+
for sc in scripts:
|
|
118
|
+
try:
|
|
119
|
+
with engine.begin() as conn:
|
|
120
|
+
ctx = MigrationContext.configure(conn)
|
|
121
|
+
# Operations.context binds the global `alembic.op` proxy these
|
|
122
|
+
# migrations import, so their `op.get_bind()` returns this
|
|
123
|
+
# connection; the with-block commits on clean exit, rolls back
|
|
124
|
+
# on any exception.
|
|
125
|
+
with Operations.context(ctx):
|
|
126
|
+
sc.module.upgrade()
|
|
112
127
|
replayed.append(sc.revision)
|
|
113
|
-
|
|
128
|
+
except Exception:
|
|
129
|
+
_log.exception("data backfill %s failed to replay; skipping it", sc.revision)
|
|
130
|
+
failures.append(sc.revision)
|
|
131
|
+
return replayed, failures
|
|
@@ -35,7 +35,12 @@ except ImportError:
|
|
|
35
35
|
config = context.config
|
|
36
36
|
|
|
37
37
|
if config.config_file_name is not None:
|
|
38
|
-
|
|
38
|
+
# disable_existing_loggers=False: alembic runs in-process during
|
|
39
|
+
# `celerp migrate`/`celerp start` and the restore reconcile, so the default
|
|
40
|
+
# (True) would silence every application logger already created, including
|
|
41
|
+
# the reconcile's own failure logger. Configure alembic's loggers without
|
|
42
|
+
# muting the app's.
|
|
43
|
+
fileConfig(config.config_file_name, disable_existing_loggers=False)
|
|
39
44
|
|
|
40
45
|
target_metadata = Base.metadata
|
|
41
46
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Copyright (c) 2026 Noah Severs
|
|
2
|
+
# SPDX-License-Identifier: BUSL-1.1
|
|
3
|
+
|
|
4
|
+
"""work center hours_per_day + is_default columns and the one-default index
|
|
5
|
+
|
|
6
|
+
Revision ID: e7c9a1b3d5f2
|
|
7
|
+
Revises: d5e6f7a8b9c0
|
|
8
|
+
Create Date: 2026-08-06
|
|
9
|
+
|
|
10
|
+
Adds hours_per_day and is_default to work_centers and the partial unique index
|
|
11
|
+
that enforces one default center per company. Verifiable DDL only (op.*), so
|
|
12
|
+
extract_signatures detects it and the auto-stamp walker never false-stamps a
|
|
13
|
+
create_all/restore database past these columns. The data move (seeding a
|
|
14
|
+
Default center, the notice, stripping the old setting) lives in the child
|
|
15
|
+
data-backfill migration f8a9b0c1d2e3. Forward-only.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import sqlalchemy as sa
|
|
21
|
+
from alembic import op
|
|
22
|
+
|
|
23
|
+
revision = "e7c9a1b3d5f2"
|
|
24
|
+
down_revision = "d5e6f7a8b9c0"
|
|
25
|
+
branch_labels = None
|
|
26
|
+
depends_on = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def upgrade() -> None:
|
|
30
|
+
op.add_column("work_centers", sa.Column("hours_per_day", sa.Float(), nullable=True))
|
|
31
|
+
# NOT NULL with a transient server default so existing rows backfill to false,
|
|
32
|
+
# then drop the default for parity with the model (celerp/models/company.py
|
|
33
|
+
# declares none). is_default is added before the index that references it.
|
|
34
|
+
op.add_column(
|
|
35
|
+
"work_centers",
|
|
36
|
+
sa.Column("is_default", sa.Boolean(), nullable=False, server_default=sa.text("false")),
|
|
37
|
+
)
|
|
38
|
+
op.create_index(
|
|
39
|
+
"uq_work_center_one_default",
|
|
40
|
+
"work_centers",
|
|
41
|
+
["company_id"],
|
|
42
|
+
unique=True,
|
|
43
|
+
postgresql_where=sa.text("is_default"),
|
|
44
|
+
)
|
|
45
|
+
op.alter_column("work_centers", "is_default", server_default=None)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def downgrade() -> None:
|
|
49
|
+
op.drop_index("uq_work_center_one_default", table_name="work_centers")
|
|
50
|
+
op.drop_column("work_centers", "is_default")
|
|
51
|
+
op.drop_column("work_centers", "hours_per_day")
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Copyright (c) 2026 Noah Severs
|
|
2
|
+
# SPDX-License-Identifier: BUSL-1.1
|
|
3
|
+
|
|
4
|
+
"""seed a Default work center and move the hours_per_day setting onto it
|
|
5
|
+
|
|
6
|
+
Revision ID: f8a9b0c1d2e3
|
|
7
|
+
Revises: e7c9a1b3d5f2
|
|
8
|
+
Create Date: 2026-08-06
|
|
9
|
+
|
|
10
|
+
The data half of the work-center default split. Signature-less (raw SQL), so
|
|
11
|
+
the develop-to-release reconcile replays it on every create_all/restore
|
|
12
|
+
database once the schema migration has added the columns. Seeds one Default
|
|
13
|
+
center per manufacturing-active company carrying the old company hours value
|
|
14
|
+
(the module's x-or-8.0 mapping), posts one company-wide notice that the setting
|
|
15
|
+
moved, and strips the now-migrated settings key. Idempotent: every step guards
|
|
16
|
+
against a prior application, because it replays on each version-change
|
|
17
|
+
reconcile and, on a restore boot, can be both alembic-applied and
|
|
18
|
+
reconcile-replayed in one pass. Forward-only.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import sqlalchemy as sa
|
|
24
|
+
from alembic import op
|
|
25
|
+
|
|
26
|
+
revision = "f8a9b0c1d2e3"
|
|
27
|
+
down_revision = "e7c9a1b3d5f2"
|
|
28
|
+
branch_labels = None
|
|
29
|
+
depends_on = None
|
|
30
|
+
|
|
31
|
+
_NOTICE_TITLE = "Hours per day moved to work centers"
|
|
32
|
+
_NOTICE_BODY = (
|
|
33
|
+
"Your company Hours per day setting now lives on the new Default work center under "
|
|
34
|
+
"Settings > Manufacturing > Work centers. Nothing changed in your To-Make estimates."
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def upgrade() -> None:
|
|
39
|
+
conn = op.get_bind()
|
|
40
|
+
|
|
41
|
+
# One dedicated Default center per manufacturing-active company, carrying its
|
|
42
|
+
# old hours value. The nested CASE mirrors the module's `x or 8.0`: a stored
|
|
43
|
+
# 0, blank, absent, or non-numeric value maps to 8.0. The regex guard runs
|
|
44
|
+
# BEFORE the ::float cast because Postgres does not guarantee AND
|
|
45
|
+
# short-circuit, so a flat `regex AND value::float` would still cast 'abc'
|
|
46
|
+
# and abort the whole insert. settings is a JSON column, so the ? membership
|
|
47
|
+
# test needs a jsonb cast; the -> / ->> extraction works on json directly.
|
|
48
|
+
conn.execute(sa.text("""
|
|
49
|
+
INSERT INTO work_centers (id, company_id, name, hours_per_day, is_default, created_at)
|
|
50
|
+
SELECT gen_random_uuid(), c.id, 'Default',
|
|
51
|
+
CASE WHEN (c.settings->'manufacturing'->>'hours_per_day') ~ '^[0-9]+(\\.[0-9]+)?$'
|
|
52
|
+
THEN CASE WHEN (c.settings->'manufacturing'->>'hours_per_day')::float <> 0
|
|
53
|
+
THEN (c.settings->'manufacturing'->>'hours_per_day')::float
|
|
54
|
+
ELSE 8.0 END
|
|
55
|
+
ELSE 8.0 END,
|
|
56
|
+
true, NOW()
|
|
57
|
+
FROM companies c
|
|
58
|
+
WHERE (c.settings::jsonb ? 'manufacturing'
|
|
59
|
+
OR EXISTS (SELECT 1 FROM work_centers wc WHERE wc.company_id = c.id))
|
|
60
|
+
AND NOT EXISTS (SELECT 1 FROM work_centers d WHERE d.company_id = c.id AND d.is_default)
|
|
61
|
+
"""))
|
|
62
|
+
|
|
63
|
+
# One company-wide notice per company that just received a Default center
|
|
64
|
+
# (user_id NULL = company-wide). Guarded so a re-run never doubles it. The
|
|
65
|
+
# notifications table is created when the application boots, not by the
|
|
66
|
+
# migration chain, so on a database migrated before its first boot it is
|
|
67
|
+
# absent. Such a database has never run the app and therefore holds no
|
|
68
|
+
# company whose value could have moved, so there is nothing to notify. The
|
|
69
|
+
# regclass check is unqualified so it resolves against the same search path
|
|
70
|
+
# the INSERT targets (public in production), not a hardcoded schema.
|
|
71
|
+
if conn.execute(sa.text("SELECT to_regclass('notifications')")).scalar() is not None:
|
|
72
|
+
conn.execute(sa.text("""
|
|
73
|
+
INSERT INTO notifications
|
|
74
|
+
(id, company_id, user_id, category, title, body, action_url, priority, read, created_at)
|
|
75
|
+
SELECT gen_random_uuid(), w.company_id, NULL, 'manufacturing', :title, :body,
|
|
76
|
+
'/settings/manufacturing', 'high', false, NOW()
|
|
77
|
+
FROM work_centers w
|
|
78
|
+
WHERE w.is_default
|
|
79
|
+
AND w.name = 'Default'
|
|
80
|
+
AND NOT EXISTS (SELECT 1 FROM notifications n
|
|
81
|
+
WHERE n.company_id = w.company_id AND n.title = :title)
|
|
82
|
+
"""), {"title": _NOTICE_TITLE, "body": _NOTICE_BODY})
|
|
83
|
+
|
|
84
|
+
# Strip the now-migrated key (jsonb #- needs the cast, then back to json).
|
|
85
|
+
conn.execute(sa.text(
|
|
86
|
+
"UPDATE companies SET settings = (settings::jsonb #- '{manufacturing,hours_per_day}')::json "
|
|
87
|
+
"WHERE settings::jsonb #> '{manufacturing,hours_per_day}' IS NOT NULL"
|
|
88
|
+
))
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def downgrade() -> None:
|
|
92
|
+
conn = op.get_bind()
|
|
93
|
+
conn.execute(sa.text("DELETE FROM work_centers WHERE is_default AND name = 'Default'"))
|
|
@@ -42,9 +42,17 @@ class WorkCenter(Base):
|
|
|
42
42
|
|
|
43
43
|
Optionally carries a WIP location, an hourly labor rate and a capacity (capacity is reserved for
|
|
44
44
|
later scheduling work). Runs may reference a work centre via work_center_id.
|
|
45
|
+
|
|
46
|
+
hours_per_day is the working day length used to turn daily labour into hours; the
|
|
47
|
+
company's default centre supplies it to the To-Make board. A partial unique index
|
|
48
|
+
keeps at most one default centre per company.
|
|
45
49
|
"""
|
|
46
50
|
__tablename__ = "work_centers"
|
|
47
|
-
__table_args__ = (
|
|
51
|
+
__table_args__ = (
|
|
52
|
+
UniqueConstraint("company_id", "name", name="uq_work_center_company_name"),
|
|
53
|
+
sa.Index("uq_work_center_one_default", "company_id",
|
|
54
|
+
unique=True, postgresql_where=sa.text("is_default")),
|
|
55
|
+
)
|
|
48
56
|
|
|
49
57
|
id: Mapped[uuid.UUID] = mapped_column(sa.Uuid(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
50
58
|
company_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("companies.id"), nullable=False)
|
|
@@ -52,6 +60,8 @@ class WorkCenter(Base):
|
|
|
52
60
|
wip_location_id: Mapped[uuid.UUID | None] = mapped_column(sa.Uuid(as_uuid=True), nullable=True)
|
|
53
61
|
labor_rate: Mapped[float | None] = mapped_column(sa.Float, nullable=True)
|
|
54
62
|
capacity: Mapped[float | None] = mapped_column(sa.Float, nullable=True)
|
|
63
|
+
hours_per_day: Mapped[float | None] = mapped_column(sa.Float, nullable=True)
|
|
64
|
+
is_default: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
|
|
55
65
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=lambda: datetime.now(timezone.utc), nullable=False)
|
|
56
66
|
|
|
57
67
|
|
|
@@ -60,7 +60,9 @@ async def fire_lifecycle(slot: str, **kwargs) -> None:
|
|
|
60
60
|
|
|
61
61
|
Each contribution must have a "handler" key pointing to a dotted path
|
|
62
62
|
"module.path:function_name". The function is called with **kwargs.
|
|
63
|
-
|
|
63
|
+
A failing hook never blocks its siblings or boot: it is logged at ERROR
|
|
64
|
+
(with traceback) and swallowed, so a recurrence surfaces as an alert
|
|
65
|
+
instead of vanishing.
|
|
64
66
|
"""
|
|
65
67
|
import importlib
|
|
66
68
|
import logging
|
|
@@ -77,7 +79,7 @@ async def fire_lifecycle(slot: str, **kwargs) -> None:
|
|
|
77
79
|
func = getattr(mod, func_name)
|
|
78
80
|
await func(**kwargs)
|
|
79
81
|
except Exception as exc:
|
|
80
|
-
_log.
|
|
82
|
+
_log.exception(
|
|
81
83
|
"Lifecycle hook %s from %s failed: %s",
|
|
82
84
|
slot, contrib.get("_module", "?"), exc,
|
|
83
85
|
)
|
|
@@ -559,10 +559,19 @@ async def list_users(company_id=Depends(get_current_company_id), session: AsyncS
|
|
|
559
559
|
return {"items": items, "total": len(items)}
|
|
560
560
|
|
|
561
561
|
|
|
562
|
+
def _assert_role_assignable(caller_role: str, target_role: str) -> None:
|
|
563
|
+
"""A holder of manage_users may not assign a role above their own. The matrix can
|
|
564
|
+
grant manage_users down to any role, so this ceiling is what stops that from
|
|
565
|
+
becoming a self-promotion path."""
|
|
566
|
+
if ROLE_LEVELS[target_role] > ROLE_LEVELS[caller_role]:
|
|
567
|
+
raise HTTPException(status_code=403, detail="You cannot assign a role above your own.")
|
|
568
|
+
|
|
569
|
+
|
|
562
570
|
@router.post("/me/users")
|
|
563
571
|
async def create_user(
|
|
564
572
|
payload: UserCreate,
|
|
565
573
|
company_id=Depends(get_current_company_id),
|
|
574
|
+
caller_role: str = Depends(get_current_role),
|
|
566
575
|
_: None = require_permission("manage_users"),
|
|
567
576
|
session: AsyncSession = Depends(get_session),
|
|
568
577
|
) -> dict:
|
|
@@ -570,6 +579,7 @@ async def create_user(
|
|
|
570
579
|
|
|
571
580
|
if payload.role not in ROLE_LEVELS:
|
|
572
581
|
raise HTTPException(400, f"Invalid role. Must be one of: {', '.join(sorted(ROLE_LEVELS, key=ROLE_LEVELS.get))}")
|
|
582
|
+
_assert_role_assignable(caller_role, payload.role)
|
|
573
583
|
|
|
574
584
|
# Check if user with this email already exists globally; if so, just link them.
|
|
575
585
|
existing_user = (await session.execute(select(User).where(User.email == payload.email))).scalar_one_or_none()
|
|
@@ -615,6 +625,7 @@ async def patch_user(
|
|
|
615
625
|
user_id: uuid.UUID,
|
|
616
626
|
payload: UserPatch,
|
|
617
627
|
company_id=Depends(get_current_company_id),
|
|
628
|
+
caller_role: str = Depends(get_current_role),
|
|
618
629
|
_: None = require_permission("manage_users"),
|
|
619
630
|
session: AsyncSession = Depends(get_session),
|
|
620
631
|
) -> dict:
|
|
@@ -627,11 +638,16 @@ async def patch_user(
|
|
|
627
638
|
)).scalar_one_or_none()
|
|
628
639
|
if not user or not link:
|
|
629
640
|
raise HTTPException(status_code=404, detail="User not found")
|
|
641
|
+
# A holder of manage_users may not modify a user whose role outranks their own.
|
|
642
|
+
if ROLE_LEVELS.get(link.role, 0) > ROLE_LEVELS[caller_role]:
|
|
643
|
+
raise HTTPException(status_code=403, detail="You cannot modify a user whose role is above your own.")
|
|
630
644
|
if payload.name is not None:
|
|
631
645
|
user.name = payload.name
|
|
632
646
|
if payload.role is not None:
|
|
633
647
|
if payload.role not in ROLE_LEVELS:
|
|
634
648
|
raise HTTPException(400, f"Invalid role. Must be one of: {', '.join(sorted(ROLE_LEVELS, key=ROLE_LEVELS.get))}")
|
|
649
|
+
# Nor may they promote anyone above their own role.
|
|
650
|
+
_assert_role_assignable(caller_role, payload.role)
|
|
635
651
|
# Guard: cannot demote the last active owner
|
|
636
652
|
old_level = ROLE_LEVELS.get(link.role, 0)
|
|
637
653
|
new_level = ROLE_LEVELS.get(payload.role, 0)
|
|
@@ -298,10 +298,19 @@ async def _reconcile_schema() -> str | None:
|
|
|
298
298
|
from celerp.config import settings
|
|
299
299
|
|
|
300
300
|
def _sync() -> None:
|
|
301
|
-
from celerp.cli import
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
301
|
+
from celerp.cli import (
|
|
302
|
+
_apply_migrations,
|
|
303
|
+
_migration_lock,
|
|
304
|
+
_post_migration_grants,
|
|
305
|
+
_reconcile_after_migrate,
|
|
306
|
+
)
|
|
307
|
+
# Serialize under the shared migration advisory lock, the same one
|
|
308
|
+
# `celerp migrate`/`celerp start` hold, so a restore reconcile cannot
|
|
309
|
+
# run concurrently with a service migration or a second restore.
|
|
310
|
+
with _migration_lock(settings.database_url):
|
|
311
|
+
_apply_migrations(settings.database_url)
|
|
312
|
+
_post_migration_grants(settings.database_url)
|
|
313
|
+
_reconcile_after_migrate(settings.database_url)
|
|
305
314
|
|
|
306
315
|
try:
|
|
307
316
|
await asyncio.get_event_loop().run_in_executor(None, _sync)
|
|
@@ -19,6 +19,11 @@ from celerp.services.pricing import is_cost_list_name, is_derived, price_key
|
|
|
19
19
|
# "Mixed", stored and displayed verbatim — no separate lowercase sentinel vs capitalized label.
|
|
20
20
|
MIXED_VALUE = "Mixed"
|
|
21
21
|
|
|
22
|
+
# Item amount fields hand-edited on the item surface, gated by the
|
|
23
|
+
# edit_inventory_amounts permission. One source of truth, mirroring the
|
|
24
|
+
# COST_ITEM_KEYS precedent in celerp.services.cost_visibility.
|
|
25
|
+
AMOUNT_ITEM_KEYS: frozenset[str] = frozenset({"quantity", "weight", "pieces", "gross_weight"})
|
|
26
|
+
|
|
22
27
|
# Default price lists (used when company has none configured)
|
|
23
28
|
_DEFAULT_PRICE_LISTS: list[dict] = [
|
|
24
29
|
{"name": "Wholesale"},
|
|
@@ -51,6 +56,7 @@ _BASE_FIELDS: list[dict] = [
|
|
|
51
56
|
{"key": "barcode", "label": "Barcode", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 104, "show_in_table": True, "tooltip_key": "field.tooltip.barcode"},
|
|
52
57
|
{"key": "hs_code", "label": "HS Code", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 104.5, "show_in_table": False, "tooltip_key": "field.tooltip.hs_code"},
|
|
53
58
|
{"key": "country_of_origin", "label": "Country of Origin", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 104.52,"show_in_table": False, "tooltip_key": "field.tooltip.country_of_origin"},
|
|
59
|
+
{"key": "preferred_supplier","label": "Preferred Supplier","type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 104.54,"show_in_table": False, "tooltip_key": "field.tooltip.preferred_supplier"},
|
|
54
60
|
{"key": "batch_no", "label": "Batch / Lot No", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 104.55,"show_in_table": False, "tooltip_key": "field.tooltip.batch_no"},
|
|
55
61
|
{"key": "purchase_sku", "label": "Purchase SKU", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 104.6, "show_in_table": False, "tooltip_key": "field.tooltip.purchase_sku"},
|
|
56
62
|
{"key": "purchase_name", "label": "Purchase Name", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 104.7, "show_in_table": False, "tooltip_key": "field.tooltip.purchase_name"},
|