celerp 2.0.0__tar.gz → 2.1.1__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.1}/PKG-INFO +1 -1
- {celerp-2.0.0 → celerp-2.1.1}/celerp/cli.py +51 -22
- {celerp-2.0.0 → celerp-2.1.1}/celerp/events/schemas.py +7 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/main.py +28 -2
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/_data_reconcile.py +30 -12
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/env.py +6 -1
- celerp-2.1.1/celerp/migrations/versions/b3c4d5e6f7a8_expand_role_permissions_to_role_grants.py +175 -0
- celerp-2.1.1/celerp/migrations/versions/e7c9a1b3d5f2_work_center_default_columns.py +51 -0
- celerp-2.1.1/celerp/migrations/versions/f8a9b0c1d2e3_work_center_default_backfill.py +93 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/models/company.py +11 -1
- {celerp-2.0.0 → celerp-2.1.1}/celerp/modules/slots.py +4 -2
- {celerp-2.0.0 → celerp-2.1.1}/celerp/routers/companies.py +45 -20
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/backup_import.py +13 -4
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/field_schema.py +13 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/permissions.py +54 -43
- celerp-2.1.1/celerp/services/reorder.py +372 -0
- celerp-2.1.1/celerp/services/status_doc_backfill.py +101 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp.egg-info/PKG-INFO +1 -1
- {celerp-2.0.0 → celerp-2.1.1}/celerp.egg-info/SOURCES.txt +6 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp.egg-info/scm_file_list.json +959 -945
- celerp-2.1.1/celerp.egg-info/scm_version.json +8 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-inventory/celerp_inventory/routes.py +135 -11
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/__init__.py +6 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/celerp_manufacturing/routes.py +189 -15
- celerp-2.1.1/default_modules/celerp-manufacturing/tests/test_auto_complete_on_finalize.py +171 -0
- celerp-2.1.1/default_modules/celerp-manufacturing/tests/test_work_centers_and_settings.py +374 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/first_party.lock.json +2 -2
- {celerp-2.0.0 → celerp-2.1.1}/test_helpers.py +24 -7
- {celerp-2.0.0 → celerp-2.1.1}/ui/api_client.py +5 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/components/activity.py +34 -1
- {celerp-2.0.0 → celerp-2.1.1}/ui/components/cloud_gate.py +37 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/components/shell.py +222 -6
- {celerp-2.0.0 → celerp-2.1.1}/ui/components/table.py +41 -12
- {celerp-2.0.0 → celerp-2.1.1}/ui/locales/ar.json +4 -1
- {celerp-2.0.0 → celerp-2.1.1}/ui/locales/de.json +4 -1
- {celerp-2.0.0 → celerp-2.1.1}/ui/locales/en.json +12 -1
- {celerp-2.0.0 → celerp-2.1.1}/ui/locales/es.json +4 -1
- {celerp-2.0.0 → celerp-2.1.1}/ui/locales/fr.json +4 -1
- {celerp-2.0.0 → celerp-2.1.1}/ui/locales/id.json +4 -1
- {celerp-2.0.0 → celerp-2.1.1}/ui/locales/it.json +4 -1
- {celerp-2.0.0 → celerp-2.1.1}/ui/locales/ja.json +4 -1
- {celerp-2.0.0 → celerp-2.1.1}/ui/locales/pt.json +4 -1
- {celerp-2.0.0 → celerp-2.1.1}/ui/locales/th.json +4 -1
- {celerp-2.0.0 → celerp-2.1.1}/ui/locales/vi.json +4 -1
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/account.py +33 -6
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/contacts.py +3 -2
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/documents.py +8 -6
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/inventory.py +883 -446
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/manufacturing.py +74 -17
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/settings.py +11 -10
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/settings_accounting.py +1 -1
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/settings_connectors.py +2 -14
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/settings_inventory.py +33 -9
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/settings_manufacturing.py +31 -26
- celerp-2.1.1/ui/security.py +19 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/static/app.css +76 -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.1}/CLA.md +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/CONTRIBUTING.md +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/LICENSING.md +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/MANIFEST.in +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/NOTICE +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/README.md +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/alembic.ini +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/__main__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/ai/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/ai/batch.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/ai/cleanup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/ai/commands.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/ai/conversations.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/ai/files.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/ai/intent.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/ai/llm.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/ai/memory.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/ai/models.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/ai/page_count.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/ai/quota.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/ai/service.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/ai/tools.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/alembic.ini +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/alembic_config.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/compat.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/compute/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/compute/aggregations.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/compute/valuation.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/config.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/connectors/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/connectors/base.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/connectors/daily_scheduler.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/connectors/http.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/connectors/images.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/connectors/quickbooks.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/connectors/registry.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/connectors/relay_token.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/connectors/shopify.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/connectors/sync_runner.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/connectors/upsert.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/connectors/util.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/connectors/webhooks.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/connectors/woocommerce.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/connectors/xero.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/constants.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/db.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/embedded_pg.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/events/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/events/engine.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/events/types.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/gateway/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/gateway/client.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/gateway/state.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/importers/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/importers/importer.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/importers/schema.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/middleware.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/README.md +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/_auto_stamp.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/_json_compat.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/script.py.mako +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/a0b1c2d3e4f5_seed_landed_cost_clearing_accounts.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/a1b2c3d4e5f6_add_doc_share_tokens.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/b1c2d3e4f5a6_add_supporter_badges.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/b2c3d4e5f6a7_add_import_batches.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/c1d2e3f4a5b6_add_marketplace.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/c2d3e4f5a6b7_add_is_sync_to_shopify.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/c4d5e6f7a8b9_account_cash_flow_category.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/d0e1f2a3b4c5_add_share_token_expiry.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/d2e3f4a5b6c7_add_password_reset_token.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/d5e6f7a8b9c0_rename_fx_rounding_to_fx_difference.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/e3f4a5b6c7d8_add_company_is_active.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/e4f5a6b7c8d9_backfill_connector_idempotency_key.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/f1a2b3c4d5e6_move_accounts_marketplace_to_modules.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/f6a7b8c9d0e1_seed_fx_rounding_account.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/fd5de461e14e_initial_schema.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/g1h2i3j4k5l6_add_consignment_flag.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/h2i3j4k5l6m7_add_bank_accounts_and_reconciliation.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/j4k5l6m7n8o9_labels_db_persistence.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/k5l6m7n8o9p0_add_reconciliation_v2.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/l6m7n8o9p0q1_add_sync_runs_table.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/m7n8o9p0q1r2_add_connector_configs_and_outbound_queue.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/n8o9p0q1r2s3_backfill_payment_date.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/o9p0q1r2s3t4_backfill_item_price_fields.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/p1q2r3s4t5u6_add_session_registry_and_user_auth_state.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/q2r3s4t5u6v7_add_system_runtime_state.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/r3s4t5u6v7w8_add_projections_company_entity_type_index.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/s4t5u6v7w8x9_drop_user_company_id_role.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/t5u6v7w8x9y0_seed_inventory_sub_accounts.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/u6v7w8x9y0z1_backfill_item_prices_from_attributes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/v7w8x9y0z1a2_backfill_purchase_unit_defaults.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/w8x9y0z1a2b3_backfill_category_unit_defaults.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/x7y8z9a0b1c2_add_projection_created_at.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/y8z9a0b1c2d3_per_company_ledger_idempotency.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/migrations/versions/z9a0b1c2d3e4_work_centers.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/models/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/models/accounting.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/models/ai.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/models/auth.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/models/base.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/models/connector_config.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/models/import_batch.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/models/ledger.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/models/marketplace.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/models/notification.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/models/projections.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/models/share.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/models/supporter.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/models/sync_run.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/modules/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/modules/api.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/modules/audit.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/modules/demotion.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/modules/importer.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/modules/license.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/modules/loader.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/modules/meta.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/modules/migrations_runner.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/modules/references.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/modules/registry.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/notifications/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/notifications/service.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/notifications/sse.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/output/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/output/branding.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/output/doc_print.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/output/pdf.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/output/share_render.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/projections/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/projections/engine.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/projections/handlers/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/projections/handlers/marketplace.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/projections/handlers/scanning.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/projections/handlers/shopify.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/projections/handlers/system.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/routers/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/routers/auth.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/routers/debug.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/routers/doctor.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/routers/events.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/routers/health.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/routers/ledger.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/routers/notifications.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/routers/stars.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/routers/system.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/schemas/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/activity_redaction.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/attachments.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/auth.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/auto_je.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/backup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/backup_export.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/backup_repo.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/backup_scheduler.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/backup_state.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/cost_visibility.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/demo.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/dev_release_guard.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/email.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/fulfill.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/holdings.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/je_keys.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/landed_cost.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/line_measures.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/list_behavior.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/money.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/payments.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/pick.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/pricing.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/relay_share.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/runtime_state.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/session_tracker.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/shipping.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/star_cta.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/supporter_badge.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/system_health.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/services/units.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/session_gate.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp/tax_regimes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp.egg-info/dependency_links.txt +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp.egg-info/entry_points.txt +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp.egg-info/requires.txt +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/celerp.egg-info/top_level.txt +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/conftest.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/celerp_accounting/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/celerp_accounting/api_setup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/celerp_accounting/csv_parser.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/celerp_accounting/matcher.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/celerp_accounting/models.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/celerp_accounting/projections.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/celerp_accounting/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/celerp_accounting/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/report_goldens/balance-sheet.html +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/report_goldens/cash-flow.html +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/report_goldens/extended-journal-csv.csv +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/report_goldens/extended-journal-print.html +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/report_goldens/extended-journal.html +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/report_goldens/general-ledger.html +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/report_goldens/journal-csv.csv +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/report_goldens/journal-print.html +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/report_goldens/journal.html +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/report_goldens/ledger.html +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/report_goldens/pnl.html +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/report_goldens/statement.html +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/report_goldens/trial-balance.html +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/test_accounting_banking.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/test_accounting_banking_routers.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/test_accounting_gaps.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/test_accounting_import_idempotency.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/test_cash_flow.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/test_financial_reports_ui.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/test_journal_gl_soa.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/test_journal_gl_soa_ui.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/test_reconciliation_v2.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/test_reports_end_to_end.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-accounting/tests/test_route_permissions.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-admin/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-admin/celerp_admin/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-admin/celerp_admin/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-admin/celerp_admin/setup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/celerp_ai/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/celerp_ai/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/celerp_ai/setup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/celerp_ai/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/tests/conftest.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/tests/test_batch.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/tests/test_cleanup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/tests/test_conversations.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/tests/test_coverage_gaps.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/tests/test_intent.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/tests/test_llm.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/tests/test_memory.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/tests/test_models.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/tests/test_page_count.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/tests/test_quota.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/tests/test_router.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/tests/test_routes_coverage.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/tests/test_service.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-ai/tests/test_tools.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-backup/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-backup/celerp_backup/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-backup/celerp_backup/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-backup/celerp_backup/setup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/celerp_connectors/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/celerp_connectors/models.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/celerp_connectors/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/tests/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/tests/conftest.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/tests/test_compliance.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/tests/test_connector_correctness.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/tests/test_connectors.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/tests/test_credentials_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/tests/test_images.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/tests/test_outbound.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/tests/test_quickbooks.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/tests/test_rate_limiting.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/tests/test_shopify.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/tests/test_wiring.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/tests/test_woocommerce.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/tests/test_woocommerce_webhooks.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-connectors/tests/test_xero.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-contacts/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-contacts/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-contacts/celerp_contacts/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-contacts/celerp_contacts/migrations.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-contacts/celerp_contacts/projections.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-contacts/celerp_contacts/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-contacts/celerp_contacts/services.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-contacts/celerp_contacts/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-dashboard/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-dashboard/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-dashboard/celerp_dashboard/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-dashboard/celerp_dashboard/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-dashboard/celerp_dashboard/setup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/celerp_docs/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/celerp_docs/api_setup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/celerp_docs/doc_constants.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/celerp_docs/doc_email.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/celerp_docs/doc_projections.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/celerp_docs/doc_service.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/celerp_docs/models_share.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/celerp_docs/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/celerp_docs/routes_payments.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/celerp_docs/routes_share.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/celerp_docs/sequences.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/celerp_docs/taxes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/celerp_docs/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/tests/conftest.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/tests/test_audits.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/tests/test_freight_je.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/tests/test_freight_lines.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/tests/test_fulfill_no_split.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/tests/test_fulfillment_activity.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/tests/test_inbound_je_lifecycle.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/tests/test_inventory_account_reconciliation.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/tests/test_landed_receive.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/tests/test_line_delete_persist.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/tests/test_line_identifier.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/tests/test_payment_void_activity.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/tests/test_receive_sku_batch.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/tests/test_send_visibility.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/tests/test_service_lines.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-docs/tests/test_void_guards.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-inventory/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-inventory/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-inventory/celerp_inventory/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-inventory/celerp_inventory/models_import_batch.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-inventory/celerp_inventory/projections.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-inventory/celerp_inventory/routes_attachments.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-inventory/celerp_inventory/routes_scanning.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-inventory/celerp_inventory/services.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-inventory/celerp_inventory/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-inventory/tests/test_item_recipe_projection.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-inventory/tests/test_item_workflow_projection.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-inventory/tests/test_landed_cost_projection.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-labels/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-labels/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-labels/celerp_labels/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-labels/celerp_labels/models.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-labels/celerp_labels/presets.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-labels/celerp_labels/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-labels/celerp_labels/service.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-labels/celerp_labels/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-labels/requirements.txt +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-labels/tests/test_labels.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-labels/tests/test_labels_module.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/celerp_manufacturing/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/celerp_manufacturing/costing.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/celerp_manufacturing/expansion.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/celerp_manufacturing/labor.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/celerp_manufacturing/migrations.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/celerp_manufacturing/projection_handler.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/celerp_manufacturing/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/requirements.txt +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/tests/test_bom_removed.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/tests/test_build_api.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/tests/test_item_recipe_api.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/tests/test_item_workflow_api.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/tests/test_labor_providers.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/tests/test_manufacture_from_doc.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/tests/test_manufacturing.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/tests/test_manufacturing_import_idempotency.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/tests/test_manufacturing_module.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/tests/test_mfg_events.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/tests/test_phase2_manufacturing_validations.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/tests/test_production_order.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/tests/test_recipe_costing.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/tests/test_recipe_expansion.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/tests/test_recost_api.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/tests/test_to_make_allocation.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/tests/test_where_used.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-manufacturing/tests/test_workflow_time.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-reports/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-reports/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-reports/celerp_reports/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-reports/celerp_reports/api_setup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-reports/celerp_reports/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-reports/celerp_reports/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-reports/tests/test_reports_full.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-reports/tests/test_reports_unit.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-subscriptions/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-subscriptions/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-subscriptions/celerp_subscriptions/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-subscriptions/celerp_subscriptions/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-subscriptions/celerp_subscriptions/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-subscriptions/tests/test_subscriptions.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-subscriptions/tests/test_subscriptions_ui.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/LICENSE +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/accessory_fashion.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/activewear.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/art_photography.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/audio_equipment.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/bag_handbag.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/banknote.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/beauty_tool.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/bed_bedroom.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/beer.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/beverage_nonalc.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/body_part_auto.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/book.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/bottoms.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/brake_suspension.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/bullion_coin.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/camera.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/colored_stone.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/comic.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/commercial_space.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/component_part.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/confectionery.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/consulting_service.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/decorative_art.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/diamond.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/drawing.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/dress_jumpsuit.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/electrical_auto.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/electrical_component.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/emerald.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/engine_part.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/fastener.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/fertilizer_chemical.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/film_video.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/fine_writing_instrument.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/fluid_lubricant.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/footwear.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/fragrance.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/fresh_food.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/fresh_produce.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/frozen_food.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/gaming_console.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/gold_bullion.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/grain_cereal.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/haircare.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/hand_tool.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/ingredient_bulk.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/interior_part.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/jewelry.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/kitchen_dining.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/laptop.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/lighting.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/livestock_feed.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/makeup.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/measuring_instrument.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/medal_token.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/mineral_specimen.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/mobile_phone.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/music_cd.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/music_vinyl.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/nail.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/numismatic_coin.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/outdoor_furniture.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/outerwear.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/packaged_food.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/painting.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/parking_bay.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/pearl.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/personal_care.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/pipe_plumbing.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/platinum_bullion.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/power_tool.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/print_edition.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/residential_unit.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/rough_gemstone.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/ruby.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/saas_plan.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/safety_ppe.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/sapphire.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/sculpture.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/seating.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/seeds.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/silver_bullion.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/skincare.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/soft_furnishing.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/software_addon.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/software_license.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/spirit.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/storage_furniture.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/swimwear.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/table_desk.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/tablet.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/tire_wheel.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/tops.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/trading_card.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/tv_display.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/video_game.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/watch.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/watch_strap.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/categories/wine.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/presets/agricultural.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/presets/artwork.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/presets/automotive.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/presets/blank.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/presets/books_media.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/presets/coins_precious_metals.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/presets/consulting.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/presets/cosmetics.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/presets/electronics.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/presets/fashion.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/presets/food_beverage.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/presets/furniture.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/presets/gemstones.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/presets/hardware.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/presets/property_rental.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/presets/saas.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/presets/watches.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/presets/wine_spirits.json +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/celerp_verticals/ui_routes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/tests/test_verticals.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/default_modules/celerp-verticals/tests/test_verticals_routers.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/legal/OFFICIAL-COMPONENTS.md +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/legal/TRADEMARK.md +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/packaging/winget/Celerp.Celerp.installer.yaml +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/packaging/winget/Celerp.Celerp.locale.en-US.yaml +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/packaging/winget/Celerp.Celerp.yaml +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/packaging/winget/README.md +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/pyproject.toml +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/setup.cfg +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/app.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/components/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/components/attrs.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/components/backup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/components/currency.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/components/files.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/components/journal.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/components/notes.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/components/phone.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/components/report_kit.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/config.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/i18n.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/marketplace_catalog.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/__init__.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/accounting.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/accounting_import.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/auth.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/csv_import.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/dashboard.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/docs_import.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/events.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/financial_reports.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/lists_import.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/modules_page.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/notifications.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/reconciliation.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/reports.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/search.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/settings_cloud.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/settings_contacts.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/settings_general.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/settings_import.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/settings_payments.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/settings_purchasing.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/settings_sales.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/setup.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/stars.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/subscriptions.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/routes/subscriptions_import.py +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/static/htmx.min.js +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/static/icon.png +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/static/logo.png +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/static/vendor/intl-tel-input/img/flags.webp +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/static/vendor/intl-tel-input/img/flags@2x.webp +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/static/vendor/intl-tel-input/intlTelInput.min.css +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/static/vendor/intl-tel-input/intlTelInput.min.js +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/ui/static/vendor/intl-tel-input/intlTelInputWithUtils.min.js +0 -0
- {celerp-2.0.0 → celerp-2.1.1}/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
|
|
celerp-2.1.1/celerp/migrations/versions/b3c4d5e6f7a8_expand_role_permissions_to_role_grants.py
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Copyright (c) 2026 Noah Severs
|
|
2
|
+
# SPDX-License-Identifier: BUSL-1.1
|
|
3
|
+
|
|
4
|
+
"""expand the role_permissions threshold overrides into explicit role_grants sets
|
|
5
|
+
|
|
6
|
+
Revision ID: b3c4d5e6f7a8
|
|
7
|
+
Revises: f8a9b0c1d2e3
|
|
8
|
+
Create Date: 2026-08-09
|
|
9
|
+
|
|
10
|
+
The data half of the per-role permissions change. Role overrides used to be a
|
|
11
|
+
single minimum-role threshold per permission (Company.settings["role_permissions"]
|
|
12
|
+
= {permission_key: minimum_role_key}); every role at or above that threshold was
|
|
13
|
+
granted, so the roles moved as a block. They are now an explicit set of granted
|
|
14
|
+
roles per permission (Company.settings["role_grants"] = {permission_key:
|
|
15
|
+
[role_key, ...]}), each role granted independently. This migration rewrites every
|
|
16
|
+
stored threshold into the exact set of roles it used to grant, so no company's
|
|
17
|
+
effective permissions change, then drops the retired key.
|
|
18
|
+
|
|
19
|
+
Deltas only: it materializes a grant set for the keys a company actually
|
|
20
|
+
overrode, never the whole catalogue; unoverridden keys keep resolving to their
|
|
21
|
+
registry default. Each threshold expands to every role at or above it, clamped up
|
|
22
|
+
to the permission's floor (so a grandfathered sub-floor override lands on the
|
|
23
|
+
floor exactly as the resolver reads it). A threshold on a permission that is no
|
|
24
|
+
longer overridable, unknown to the catalogue, or naming a role that no longer
|
|
25
|
+
exists never resolved to anything but the default, so it becomes no delta and is
|
|
26
|
+
left absent.
|
|
27
|
+
|
|
28
|
+
Signature-less (data only), so the develop-to-release reconcile replays it on
|
|
29
|
+
every create_all/restore database. Idempotent: guarded on the presence of the
|
|
30
|
+
retired key, so a second application is a no-op. The downgrade is the best-effort
|
|
31
|
+
inverse: it collapses each grant set back to a single threshold at its lowest
|
|
32
|
+
granted role and restores the retired key, so a rollback to the threshold-reading
|
|
33
|
+
code keeps a company's effective permissions. This round-trips exactly for the
|
|
34
|
+
contiguous sets the upgrade produces; a hand-authored non-contiguous set is
|
|
35
|
+
documented lossy (it collapses to its lowest role, which the older resolver then
|
|
36
|
+
re-expands as a block).
|
|
37
|
+
|
|
38
|
+
The catalogue (floor role and overridability per permission) and the role levels
|
|
39
|
+
are snapshotted here as literals, because a migration is immutable history: it
|
|
40
|
+
must expand exactly as the rules stood when it was written, even after the
|
|
41
|
+
registry in celerp.services.permissions later changes.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
from __future__ import annotations
|
|
45
|
+
|
|
46
|
+
import json
|
|
47
|
+
|
|
48
|
+
import sqlalchemy as sa
|
|
49
|
+
from alembic import op
|
|
50
|
+
|
|
51
|
+
revision = "b3c4d5e6f7a8"
|
|
52
|
+
down_revision = "f8a9b0c1d2e3"
|
|
53
|
+
branch_labels = None
|
|
54
|
+
depends_on = None
|
|
55
|
+
|
|
56
|
+
# Snapshot of auth.ROLE_LEVELS at this revision.
|
|
57
|
+
_ROLE_LEVELS = {"viewer": 1, "operator": 2, "manager": 3, "admin": 4, "owner": 5}
|
|
58
|
+
|
|
59
|
+
# Snapshot of the permission catalogue at this revision: key -> (floor_role,
|
|
60
|
+
# grantable). default_role is irrelevant here because every key processed carries
|
|
61
|
+
# an explicit override; only the floor (which clamps the expansion) and whether
|
|
62
|
+
# the key is overridable at all matter.
|
|
63
|
+
_CATALOGUE = {
|
|
64
|
+
"view_dashboards": ("viewer", True),
|
|
65
|
+
"view_documents": ("viewer", True),
|
|
66
|
+
"view_contacts": ("viewer", True),
|
|
67
|
+
"view_inventory": ("viewer", True),
|
|
68
|
+
"edit_documents": ("viewer", True),
|
|
69
|
+
"edit_contacts": ("viewer", True),
|
|
70
|
+
"edit_inventory": ("viewer", True),
|
|
71
|
+
"edit_inventory_amounts": ("viewer", True),
|
|
72
|
+
"finalize_documents": ("viewer", True),
|
|
73
|
+
"fulfill_documents": ("viewer", True),
|
|
74
|
+
"record_payments": ("viewer", True),
|
|
75
|
+
"manage_labels": ("viewer", True),
|
|
76
|
+
"manage_manufacturing": ("viewer", True),
|
|
77
|
+
"use_ai_assistant": ("viewer", True),
|
|
78
|
+
"run_backups": ("viewer", True),
|
|
79
|
+
"view_subscriptions": ("viewer", True),
|
|
80
|
+
"view_inventory_costs": ("viewer", True),
|
|
81
|
+
"set_inventory_prices": ("viewer", True),
|
|
82
|
+
"set_sales_doc_prices": ("viewer", True),
|
|
83
|
+
"delete_documents": ("viewer", True),
|
|
84
|
+
"adjust_inventory": ("viewer", True),
|
|
85
|
+
"import_export_data": ("viewer", True),
|
|
86
|
+
"view_payments": ("viewer", True),
|
|
87
|
+
"view_financial_reports": ("viewer", True),
|
|
88
|
+
"manage_accounting": ("viewer", True),
|
|
89
|
+
"manage_module_settings": ("viewer", True),
|
|
90
|
+
"manage_users": ("viewer", True),
|
|
91
|
+
"manage_company_settings": ("admin", True),
|
|
92
|
+
"manage_integrations": ("viewer", True),
|
|
93
|
+
"manage_permissions": ("owner", False),
|
|
94
|
+
"manage_company_lifecycle": ("owner", False),
|
|
95
|
+
"manage_billing": ("owner", False),
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _expand(stored_role: str, floor_role: str) -> list[str]:
|
|
100
|
+
"""The role keys a threshold used to grant: every role at or above the
|
|
101
|
+
threshold, clamped up to the floor, ordered low to high."""
|
|
102
|
+
threshold = max(_ROLE_LEVELS[stored_role], _ROLE_LEVELS[floor_role])
|
|
103
|
+
return sorted(
|
|
104
|
+
(r for r, lvl in _ROLE_LEVELS.items() if lvl >= threshold),
|
|
105
|
+
key=lambda r: _ROLE_LEVELS[r],
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _collapse(granted_roles: list[str]) -> str | None:
|
|
110
|
+
"""Best-effort inverse of _expand: the threshold role at the lowest granted
|
|
111
|
+
level. Exact for a contiguous set (the shape upgrade produces); lossy for a
|
|
112
|
+
non-contiguous set, which no longer round-trips to the same set. None when no
|
|
113
|
+
granted role is known, leaving the permission absent (resolves to default)."""
|
|
114
|
+
levels = [_ROLE_LEVELS[r] for r in granted_roles if r in _ROLE_LEVELS]
|
|
115
|
+
if not levels:
|
|
116
|
+
return None
|
|
117
|
+
low = min(levels)
|
|
118
|
+
return next(r for r, lvl in _ROLE_LEVELS.items() if lvl == low)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def upgrade() -> None:
|
|
122
|
+
conn = op.get_bind()
|
|
123
|
+
# Only companies that still carry the retired key; this is also the
|
|
124
|
+
# idempotency guard, so a replay after the key is stripped touches nothing.
|
|
125
|
+
rows = conn.execute(sa.text(
|
|
126
|
+
"SELECT id, settings FROM companies WHERE settings::jsonb ? 'role_permissions'"
|
|
127
|
+
)).fetchall()
|
|
128
|
+
|
|
129
|
+
for cid, settings in rows:
|
|
130
|
+
data = settings if isinstance(settings, dict) else json.loads(settings)
|
|
131
|
+
role_perms = data.get("role_permissions")
|
|
132
|
+
grants = dict(data.get("role_grants") or {})
|
|
133
|
+
if isinstance(role_perms, dict):
|
|
134
|
+
for key, stored_role in role_perms.items():
|
|
135
|
+
meta = _CATALOGUE.get(key)
|
|
136
|
+
if meta is None:
|
|
137
|
+
continue # unknown key: never resolved to an override
|
|
138
|
+
floor_role, grantable = meta
|
|
139
|
+
if not grantable:
|
|
140
|
+
continue # fixed permission: overrides were never honored
|
|
141
|
+
if stored_role not in _ROLE_LEVELS:
|
|
142
|
+
continue # stale role: resolved to the default, no delta
|
|
143
|
+
grants[key] = _expand(stored_role, floor_role)
|
|
144
|
+
if grants:
|
|
145
|
+
data["role_grants"] = grants
|
|
146
|
+
data.pop("role_permissions", None)
|
|
147
|
+
conn.execute(
|
|
148
|
+
sa.text("UPDATE companies SET settings = CAST(:s AS json) WHERE id = :id"),
|
|
149
|
+
{"s": json.dumps(data), "id": str(cid)},
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def downgrade() -> None:
|
|
154
|
+
conn = op.get_bind()
|
|
155
|
+
# Only companies that carry the new key; this is also the idempotency guard.
|
|
156
|
+
rows = conn.execute(sa.text(
|
|
157
|
+
"SELECT id, settings FROM companies WHERE settings::jsonb ? 'role_grants'"
|
|
158
|
+
)).fetchall()
|
|
159
|
+
|
|
160
|
+
for cid, settings in rows:
|
|
161
|
+
data = settings if isinstance(settings, dict) else json.loads(settings)
|
|
162
|
+
grants = data.get("role_grants")
|
|
163
|
+
role_perms = dict(data.get("role_permissions") or {})
|
|
164
|
+
if isinstance(grants, dict):
|
|
165
|
+
for key, roles in grants.items():
|
|
166
|
+
threshold = _collapse(roles if isinstance(roles, list) else [])
|
|
167
|
+
if threshold is not None:
|
|
168
|
+
role_perms[key] = threshold
|
|
169
|
+
if role_perms:
|
|
170
|
+
data["role_permissions"] = role_perms
|
|
171
|
+
data.pop("role_grants", None)
|
|
172
|
+
conn.execute(
|
|
173
|
+
sa.text("UPDATE companies SET settings = CAST(:s AS json) WHERE id = :id"),
|
|
174
|
+
{"s": json.dumps(data), "id": str(cid)},
|
|
175
|
+
)
|
|
@@ -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
|
)
|