celerp 2.3.0__tar.gz → 2.3.2__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.3.0 → celerp-2.3.2}/PKG-INFO +1 -1
- {celerp-2.3.0 → celerp-2.3.2}/celerp/events/schemas.py +43 -4
- celerp-2.3.2/celerp/inventory_codes.py +87 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/main.py +9 -0
- celerp-2.3.2/celerp/migrations/versions/bc0d1e2f3a4b_barcode_unique_index.py +119 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/models/projections.py +16 -1
- {celerp-2.3.0 → celerp-2.3.2}/celerp/projections/engine.py +26 -2
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/field_schema.py +65 -33
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/fulfill.py +22 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/tax_regimes.py +16 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp.egg-info/PKG-INFO +1 -1
- {celerp-2.3.0 → celerp-2.3.2}/celerp.egg-info/SOURCES.txt +18 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp.egg-info/scm_file_list.json +71 -0
- celerp-2.3.2/celerp.egg-info/scm_version.json +8 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/report_goldens/statement.html +56 -56
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/celerp_ai/ui_routes.py +9 -8
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-backup/celerp_backup/routes.py +1 -1
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/celerp_docs/routes.py +225 -90
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/tests/test_audits.py +326 -13
- celerp-2.3.2/default_modules/celerp-docs/tests/test_list_concurrency.py +178 -0
- celerp-2.3.2/default_modules/celerp-docs/tests/test_list_outcome_concurrency.py +364 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-inventory/celerp_inventory/routes.py +141 -59
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-inventory/celerp_inventory/routes_scanning.py +3 -1
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-inventory/celerp_inventory/services.py +96 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-labels/__init__.py +19 -0
- celerp-2.3.2/default_modules/celerp-labels/celerp_labels/locales/am.json +11 -0
- celerp-2.3.2/default_modules/celerp-labels/celerp_labels/locales/ar.json +11 -0
- celerp-2.3.2/default_modules/celerp-labels/celerp_labels/locales/de.json +11 -0
- celerp-2.3.2/default_modules/celerp-labels/celerp_labels/locales/en.json +11 -0
- celerp-2.3.2/default_modules/celerp-labels/celerp_labels/locales/es.json +11 -0
- celerp-2.3.2/default_modules/celerp-labels/celerp_labels/locales/fr.json +11 -0
- celerp-2.3.2/default_modules/celerp-labels/celerp_labels/locales/id.json +11 -0
- celerp-2.3.2/default_modules/celerp-labels/celerp_labels/locales/it.json +11 -0
- celerp-2.3.2/default_modules/celerp-labels/celerp_labels/locales/ja.json +11 -0
- celerp-2.3.2/default_modules/celerp-labels/celerp_labels/locales/pt.json +11 -0
- celerp-2.3.2/default_modules/celerp-labels/celerp_labels/locales/th.json +11 -0
- celerp-2.3.2/default_modules/celerp-labels/celerp_labels/locales/vi.json +11 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-labels/celerp_labels/ui_routes.py +10 -9
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/celerp_manufacturing/routes.py +25 -0
- celerp-2.3.2/default_modules/celerp-manufacturing/tests/test_completion_lock_order.py +240 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_run_records.py +18 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/first_party.lock.json +7 -7
- {celerp-2.3.0 → celerp-2.3.2}/ui/api_client.py +18 -5
- {celerp-2.3.0 → celerp-2.3.2}/ui/app.py +10 -8
- {celerp-2.3.0 → celerp-2.3.2}/ui/components/activity.py +133 -178
- {celerp-2.3.0 → celerp-2.3.2}/ui/components/backup.py +13 -8
- {celerp-2.3.0 → celerp-2.3.2}/ui/components/cloud_gate.py +12 -9
- {celerp-2.3.0 → celerp-2.3.2}/ui/components/currency.py +1 -1
- {celerp-2.3.0 → celerp-2.3.2}/ui/components/files.py +10 -5
- {celerp-2.3.0 → celerp-2.3.2}/ui/components/notes.py +2 -2
- {celerp-2.3.0 → celerp-2.3.2}/ui/components/report_kit.py +7 -7
- {celerp-2.3.0 → celerp-2.3.2}/ui/components/shell.py +194 -69
- {celerp-2.3.0 → celerp-2.3.2}/ui/components/table.py +183 -63
- {celerp-2.3.0 → celerp-2.3.2}/ui/i18n.py +14 -0
- celerp-2.3.2/ui/locales/am.json +3056 -0
- celerp-2.3.2/ui/locales/ar.json +3056 -0
- celerp-2.3.2/ui/locales/de.json +3056 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/locales/en.json +1437 -2
- celerp-2.3.2/ui/locales/es.json +3056 -0
- celerp-2.3.2/ui/locales/fr.json +3056 -0
- celerp-2.3.2/ui/locales/id.json +3056 -0
- celerp-2.3.2/ui/locales/it.json +3056 -0
- celerp-2.3.2/ui/locales/ja.json +3056 -0
- celerp-2.3.2/ui/locales/pt.json +3056 -0
- celerp-2.3.2/ui/locales/th.json +3056 -0
- celerp-2.3.2/ui/locales/vi.json +3056 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/account.py +2 -4
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/accounting.py +4 -4
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/accounting_import.py +20 -19
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/auth.py +70 -82
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/contacts.py +125 -111
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/csv_import.py +77 -52
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/dashboard.py +175 -47
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/docs_import.py +18 -18
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/documents.py +627 -412
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/financial_reports.py +24 -24
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/inventory.py +381 -346
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/lists_import.py +18 -18
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/manufacturing.py +122 -110
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/modules_page.py +2 -2
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/reconciliation.py +40 -47
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/reports.py +72 -66
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/search.py +28 -10
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/settings.py +169 -177
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/settings_accounting.py +73 -71
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/settings_cloud.py +22 -22
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/settings_connectors.py +56 -29
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/settings_contacts.py +12 -12
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/settings_general.py +5 -5
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/settings_import.py +53 -52
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/settings_inventory.py +63 -70
- celerp-2.3.2/ui/routes/settings_manufacturing.py +153 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/settings_payments.py +6 -5
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/settings_purchasing.py +6 -6
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/settings_sales.py +48 -36
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/setup.py +17 -18
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/stars.py +12 -5
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/subscriptions.py +24 -25
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/subscriptions_import.py +18 -18
- {celerp-2.3.0 → celerp-2.3.2}/ui/static/app.css +3 -2
- celerp-2.3.0/celerp.egg-info/scm_version.json +0 -8
- celerp-2.3.0/ui/locales/ar.json +0 -1520
- celerp-2.3.0/ui/locales/de.json +0 -1520
- celerp-2.3.0/ui/locales/es.json +0 -1520
- celerp-2.3.0/ui/locales/fr.json +0 -1520
- celerp-2.3.0/ui/locales/id.json +0 -1520
- celerp-2.3.0/ui/locales/it.json +0 -1520
- celerp-2.3.0/ui/locales/ja.json +0 -1520
- celerp-2.3.0/ui/locales/pt.json +0 -1520
- celerp-2.3.0/ui/locales/th.json +0 -1520
- celerp-2.3.0/ui/locales/vi.json +0 -1520
- celerp-2.3.0/ui/routes/settings_manufacturing.py +0 -152
- {celerp-2.3.0 → celerp-2.3.2}/CLA.md +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/CONTRIBUTING.md +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/LICENSE +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/LICENSING.md +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/MANIFEST.in +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/NOTICE +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/README.md +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/alembic.ini +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/__main__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/ai/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/ai/batch.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/ai/cleanup.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/ai/commands.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/ai/conversations.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/ai/files.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/ai/intent.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/ai/llm.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/ai/memory.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/ai/models.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/ai/page_count.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/ai/quota.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/ai/service.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/ai/tools.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/alembic.ini +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/alembic_config.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/cli.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/compat.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/compute/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/compute/aggregations.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/compute/valuation.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/config.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/connectors/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/connectors/base.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/connectors/daily_scheduler.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/connectors/http.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/connectors/images.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/connectors/quickbooks.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/connectors/registry.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/connectors/relay_token.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/connectors/shopify.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/connectors/sync_runner.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/connectors/upsert.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/connectors/util.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/connectors/webhooks.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/connectors/woocommerce.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/connectors/xero.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/constants.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/db.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/embedded_pg.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/events/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/events/engine.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/events/types.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/gateway/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/gateway/client.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/gateway/state.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/importers/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/importers/importer.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/importers/schema.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/middleware.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/README.md +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/_auto_stamp.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/_data_reconcile.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/_json_compat.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/env.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/script.py.mako +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/a0b1c2d3e4f5_seed_landed_cost_clearing_accounts.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/a1b2c3d4e5f6_add_doc_share_tokens.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/b1c2d3e4f5a6_add_supporter_badges.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/b2c3d4e5f6a7_add_import_batches.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/b3c4d5e6f7a8_expand_role_permissions_to_role_grants.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/c1d2e3f4a5b6_add_marketplace.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/c2d3e4f5a6b7_add_is_sync_to_shopify.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/c4d5e6f7a8b9_account_cash_flow_category.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/d0e1f2a3b4c5_add_share_token_expiry.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/d2e3f4a5b6c7_add_password_reset_token.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/d5e6f7a8b9c0_rename_fx_rounding_to_fx_difference.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/e3f4a5b6c7d8_add_company_is_active.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/e4f5a6b7c8d9_backfill_connector_idempotency_key.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/e7c9a1b3d5f2_work_center_default_columns.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/f1a2b3c4d5e6_move_accounts_marketplace_to_modules.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/f6a7b8c9d0e1_seed_fx_rounding_account.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/f8a9b0c1d2e3_work_center_default_backfill.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/fd5de461e14e_initial_schema.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/g1h2i3j4k5l6_add_consignment_flag.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/h2i3j4k5l6m7_add_bank_accounts_and_reconciliation.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/j4k5l6m7n8o9_labels_db_persistence.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/k5l6m7n8o9p0_add_reconciliation_v2.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/l6m7n8o9p0q1_add_sync_runs_table.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/m7n8o9p0q1r2_add_connector_configs_and_outbound_queue.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/n8o9p0q1r2s3_backfill_payment_date.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/o9p0q1r2s3t4_backfill_item_price_fields.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/p1q2r3s4t5u6_add_session_registry_and_user_auth_state.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/q2r3s4t5u6v7_add_system_runtime_state.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/r3s4t5u6v7w8_add_projections_company_entity_type_index.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/s4t5u6v7w8x9_drop_user_company_id_role.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/t5u6v7w8x9y0_seed_inventory_sub_accounts.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/u6v7w8x9y0z1_backfill_item_prices_from_attributes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/v7w8x9y0z1a2_backfill_purchase_unit_defaults.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/w8x9y0z1a2b3_backfill_category_unit_defaults.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/x7y8z9a0b1c2_add_projection_created_at.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/y8z9a0b1c2d3_per_company_ledger_idempotency.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/migrations/versions/z9a0b1c2d3e4_work_centers.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/models/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/models/accounting.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/models/ai.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/models/auth.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/models/base.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/models/company.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/models/connector_config.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/models/import_batch.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/models/ledger.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/models/marketplace.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/models/notification.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/models/share.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/models/supporter.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/models/sync_run.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/modules/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/modules/api.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/modules/audit.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/modules/demotion.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/modules/importer.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/modules/license.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/modules/loader.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/modules/meta.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/modules/migrations_runner.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/modules/references.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/modules/registry.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/modules/slots.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/notifications/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/notifications/service.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/notifications/sse.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/output/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/output/branding.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/output/doc_print.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/output/pdf.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/output/share_render.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/projections/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/projections/handlers/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/projections/handlers/marketplace.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/projections/handlers/scanning.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/projections/handlers/shopify.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/projections/handlers/system.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/routers/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/routers/auth.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/routers/companies.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/routers/debug.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/routers/doctor.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/routers/events.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/routers/health.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/routers/ledger.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/routers/notifications.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/routers/stars.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/routers/system.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/schemas/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/activity_redaction.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/attachments.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/auth.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/auto_je.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/backup.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/backup_export.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/backup_import.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/backup_repo.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/backup_scheduler.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/backup_state.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/cogs_backfill.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/cost_visibility.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/demo.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/dev_release_guard.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/email.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/holdings.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/je_keys.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/landed_cost.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/line_measures.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/list_behavior.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/money.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/payments.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/permissions.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/pick.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/pricing.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/relay_share.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/reorder.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/runtime_state.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/session_tracker.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/shipping.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/star_cta.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/status_doc_backfill.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/supporter_badge.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/system_health.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/services/units.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp/session_gate.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp.egg-info/dependency_links.txt +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp.egg-info/entry_points.txt +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp.egg-info/requires.txt +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/celerp.egg-info/top_level.txt +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/conftest.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/conftest_support.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/LICENSE +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/celerp_accounting/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/celerp_accounting/api_setup.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/celerp_accounting/csv_parser.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/celerp_accounting/matcher.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/celerp_accounting/models.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/celerp_accounting/projections.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/celerp_accounting/routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/celerp_accounting/ui_routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/report_goldens/balance-sheet.html +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/report_goldens/cash-flow.html +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/report_goldens/extended-journal-csv.csv +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/report_goldens/extended-journal-print.html +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/report_goldens/extended-journal.html +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/report_goldens/general-ledger.html +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/report_goldens/journal-csv.csv +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/report_goldens/journal-print.html +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/report_goldens/journal.html +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/report_goldens/ledger.html +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/report_goldens/pnl.html +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/report_goldens/trial-balance.html +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/test_accounting_banking.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/test_accounting_banking_routers.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/test_accounting_gaps.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/test_accounting_import_idempotency.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/test_cash_flow.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/test_financial_reports_ui.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/test_journal_gl_soa.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/test_journal_gl_soa_ui.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/test_reconciliation_v2.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/test_reports_end_to_end.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-accounting/tests/test_route_permissions.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-admin/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-admin/celerp_admin/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-admin/celerp_admin/routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-admin/celerp_admin/setup.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/celerp_ai/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/celerp_ai/routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/celerp_ai/setup.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/tests/conftest.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/tests/test_batch.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/tests/test_cleanup.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/tests/test_conversations.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/tests/test_coverage_gaps.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/tests/test_intent.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/tests/test_llm.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/tests/test_memory.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/tests/test_models.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/tests/test_page_count.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/tests/test_quota.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/tests/test_router.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/tests/test_routes_coverage.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/tests/test_service.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-ai/tests/test_tools.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-backup/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-backup/celerp_backup/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-backup/celerp_backup/setup.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/celerp_connectors/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/celerp_connectors/models.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/celerp_connectors/routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/tests/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/tests/conftest.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/tests/test_compliance.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/tests/test_connector_correctness.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/tests/test_connectors.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/tests/test_credentials_routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/tests/test_images.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/tests/test_outbound.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/tests/test_quickbooks.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/tests/test_rate_limiting.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/tests/test_shopify.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/tests/test_wiring.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/tests/test_woocommerce.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/tests/test_woocommerce_webhooks.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-connectors/tests/test_xero.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-contacts/LICENSE +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-contacts/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-contacts/celerp_contacts/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-contacts/celerp_contacts/migrations.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-contacts/celerp_contacts/projections.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-contacts/celerp_contacts/routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-contacts/celerp_contacts/services.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-contacts/celerp_contacts/ui_routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-dashboard/LICENSE +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-dashboard/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-dashboard/celerp_dashboard/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-dashboard/celerp_dashboard/routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-dashboard/celerp_dashboard/setup.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/LICENSE +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/celerp_docs/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/celerp_docs/api_setup.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/celerp_docs/doc_constants.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/celerp_docs/doc_email.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/celerp_docs/doc_projections.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/celerp_docs/doc_service.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/celerp_docs/models_share.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/celerp_docs/routes_payments.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/celerp_docs/routes_share.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/celerp_docs/sequences.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/celerp_docs/taxes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/celerp_docs/ui_routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/tests/conftest.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/tests/test_consolidate_lots.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/tests/test_freight_je.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/tests/test_freight_lines.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/tests/test_fulfill_no_split.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/tests/test_fulfillment_activity.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/tests/test_inbound_je_lifecycle.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/tests/test_inventory_account_reconciliation.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/tests/test_landed_receive.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/tests/test_line_delete_persist.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/tests/test_line_identifier.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/tests/test_payment_void_activity.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/tests/test_receive_sku_batch.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/tests/test_send_visibility.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/tests/test_service_lines.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/tests/test_void_guards.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-docs/tests/test_writeoffs.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-inventory/LICENSE +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-inventory/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-inventory/celerp_inventory/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-inventory/celerp_inventory/models_import_batch.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-inventory/celerp_inventory/projections.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-inventory/celerp_inventory/routes_attachments.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-inventory/celerp_inventory/ui_routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-inventory/tests/test_item_recipe_projection.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-inventory/tests/test_item_workflow_projection.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-inventory/tests/test_landed_cost_projection.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-labels/LICENSE +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-labels/celerp_labels/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-labels/celerp_labels/models.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-labels/celerp_labels/presets.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-labels/celerp_labels/routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-labels/celerp_labels/service.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-labels/requirements.txt +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-labels/tests/test_labels.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-labels/tests/test_labels_module.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/LICENSE +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/celerp_manufacturing/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/celerp_manufacturing/costing.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/celerp_manufacturing/expansion.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/celerp_manufacturing/labor.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/celerp_manufacturing/migrations.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/celerp_manufacturing/projection_handler.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/celerp_manufacturing/ui_routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/requirements.txt +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_auto_complete_on_finalize.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_bom_removed.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_build_api.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_item_recipe_api.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_item_workflow_api.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_labor_providers.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_manufacture_from_doc.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_manufacturing.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_manufacturing_import_idempotency.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_manufacturing_module.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_mfg_events.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_phase2_manufacturing_validations.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_production_order.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_recipe_costing.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_recipe_expansion.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_recost_api.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_to_make_allocation.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_where_used.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_work_centers_and_settings.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-manufacturing/tests/test_workflow_time.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-reports/LICENSE +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-reports/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-reports/celerp_reports/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-reports/celerp_reports/api_setup.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-reports/celerp_reports/routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-reports/celerp_reports/ui_routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-reports/tests/test_reports_full.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-reports/tests/test_reports_unit.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-subscriptions/LICENSE +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-subscriptions/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-subscriptions/celerp_subscriptions/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-subscriptions/celerp_subscriptions/routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-subscriptions/celerp_subscriptions/ui_routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-subscriptions/tests/test_subscriptions.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-subscriptions/tests/test_subscriptions_ui.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/LICENSE +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/accessory_fashion.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/activewear.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/art_photography.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/audio_equipment.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/bag_handbag.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/banknote.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/beauty_tool.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/bed_bedroom.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/beer.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/beverage_nonalc.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/body_part_auto.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/book.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/bottoms.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/brake_suspension.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/bullion_coin.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/camera.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/colored_stone.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/comic.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/commercial_space.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/component_part.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/confectionery.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/consulting_service.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/decorative_art.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/diamond.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/drawing.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/dress_jumpsuit.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/electrical_auto.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/electrical_component.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/emerald.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/engine_part.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/fastener.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/fertilizer_chemical.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/film_video.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/fine_writing_instrument.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/fluid_lubricant.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/footwear.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/fragrance.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/fresh_food.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/fresh_produce.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/frozen_food.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/gaming_console.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/gold_bullion.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/grain_cereal.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/haircare.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/hand_tool.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/ingredient_bulk.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/interior_part.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/jewelry.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/kitchen_dining.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/laptop.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/lighting.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/livestock_feed.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/makeup.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/measuring_instrument.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/medal_token.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/mineral_specimen.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/mobile_phone.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/music_cd.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/music_vinyl.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/nail.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/numismatic_coin.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/outdoor_furniture.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/outerwear.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/packaged_food.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/painting.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/parking_bay.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/pearl.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/personal_care.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/pipe_plumbing.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/platinum_bullion.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/power_tool.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/print_edition.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/residential_unit.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/rough_gemstone.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/ruby.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/saas_plan.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/safety_ppe.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/sapphire.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/sculpture.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/seating.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/seeds.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/silver_bullion.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/skincare.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/soft_furnishing.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/software_addon.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/software_license.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/spirit.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/storage_furniture.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/swimwear.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/table_desk.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/tablet.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/tire_wheel.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/tops.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/trading_card.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/tv_display.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/video_game.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/watch.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/watch_strap.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/categories/wine.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/presets/agricultural.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/presets/artwork.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/presets/automotive.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/presets/blank.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/presets/books_media.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/presets/coins_precious_metals.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/presets/consulting.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/presets/cosmetics.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/presets/electronics.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/presets/fashion.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/presets/food_beverage.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/presets/furniture.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/presets/gemstones.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/presets/hardware.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/presets/property_rental.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/presets/saas.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/presets/watches.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/presets/wine_spirits.json +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/celerp_verticals/ui_routes.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/tests/test_verticals.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/default_modules/celerp-verticals/tests/test_verticals_routers.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/legal/OFFICIAL-COMPONENTS.md +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/legal/TRADEMARK.md +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/packaging/winget/Celerp.Celerp.installer.yaml +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/packaging/winget/Celerp.Celerp.locale.en-US.yaml +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/packaging/winget/Celerp.Celerp.yaml +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/packaging/winget/README.md +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/pyproject.toml +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/setup.cfg +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/test_helpers.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/components/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/components/attrs.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/components/journal.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/components/phone.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/config.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/marketplace_catalog.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/__init__.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/events.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/routes/notifications.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/security.py +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/static/htmx.min.js +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/static/icon.png +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/static/logo.png +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/static/vendor/intl-tel-input/img/flags.webp +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/static/vendor/intl-tel-input/img/flags@2x.webp +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/static/vendor/intl-tel-input/intlTelInput.min.css +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/static/vendor/intl-tel-input/intlTelInput.min.js +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/static/vendor/intl-tel-input/intlTelInputWithUtils.min.js +0 -0
- {celerp-2.3.0 → celerp-2.3.2}/ui/static/vendor/intl-tel-input/utils.js +0 -0
|
@@ -5,7 +5,18 @@ from __future__ import annotations
|
|
|
5
5
|
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
-
from pydantic import BaseModel, Field
|
|
8
|
+
from pydantic import BaseModel, Field, model_validator
|
|
9
|
+
|
|
10
|
+
# The SKU/barcode write-time predicates live in celerp.inventory_codes so the event
|
|
11
|
+
# boundary, the interactive routes, the allocation service, and the scanner share one
|
|
12
|
+
# source of truth. reject_comma_sku/SKU_COMMA_MESSAGE are re-exported here because
|
|
13
|
+
# celerp_inventory.routes imports them from this module.
|
|
14
|
+
from celerp.inventory_codes import ( # noqa: F401 - re-exported for celerp_inventory
|
|
15
|
+
SKU_COMMA_MESSAGE,
|
|
16
|
+
reject_comma_sku,
|
|
17
|
+
validate_barcode,
|
|
18
|
+
validate_sku,
|
|
19
|
+
)
|
|
9
20
|
|
|
10
21
|
|
|
11
22
|
# -----------------
|
|
@@ -13,7 +24,27 @@ from pydantic import BaseModel, Field
|
|
|
13
24
|
# -----------------
|
|
14
25
|
|
|
15
26
|
|
|
16
|
-
class
|
|
27
|
+
class _SkuGuard(BaseModel):
|
|
28
|
+
"""Mixin: enforce the SKU and barcode invariants on any item event that sets them.
|
|
29
|
+
|
|
30
|
+
SKU: no comma (Celerp's OR operator in the SKU/search syntax) and a bounded length.
|
|
31
|
+
Barcode: digits only and a bounded length. These are the canonical write-time
|
|
32
|
+
predicates; interactive routes wrap them to surface a friendly 422, every other
|
|
33
|
+
emitter hits them here at the event boundary (imports, connector upserts, document
|
|
34
|
+
receiving). Replay never runs these schemas (the reducer does), so enforcing here
|
|
35
|
+
can never brick a historical projection.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
@model_validator(mode="before")
|
|
39
|
+
@classmethod
|
|
40
|
+
def _guard_item_codes(cls, data: Any) -> Any:
|
|
41
|
+
if isinstance(data, dict):
|
|
42
|
+
validate_sku(data.get("sku"))
|
|
43
|
+
validate_barcode(data.get("barcode"))
|
|
44
|
+
return data
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ItemCreated(_SkuGuard):
|
|
17
48
|
sku: str
|
|
18
49
|
name: str
|
|
19
50
|
quantity: float = 0
|
|
@@ -28,7 +59,7 @@ class ItemCreated(BaseModel):
|
|
|
28
59
|
model_config = {"extra": "allow"}
|
|
29
60
|
|
|
30
61
|
|
|
31
|
-
class ItemSnapshot(
|
|
62
|
+
class ItemSnapshot(_SkuGuard):
|
|
32
63
|
"""Snapshot of full item state - used for imports. sku/name optional (absent in some sources)."""
|
|
33
64
|
|
|
34
65
|
sku: str | None = None
|
|
@@ -53,6 +84,14 @@ class ItemSnapshot(BaseModel):
|
|
|
53
84
|
class ItemUpdated(BaseModel):
|
|
54
85
|
fields_changed: dict[str, dict[str, Any]]
|
|
55
86
|
|
|
87
|
+
@model_validator(mode="after")
|
|
88
|
+
def _guard_item_codes(self) -> "ItemUpdated":
|
|
89
|
+
if "sku" in self.fields_changed:
|
|
90
|
+
validate_sku((self.fields_changed.get("sku") or {}).get("new"))
|
|
91
|
+
if "barcode" in self.fields_changed:
|
|
92
|
+
validate_barcode((self.fields_changed.get("barcode") or {}).get("new"))
|
|
93
|
+
return self
|
|
94
|
+
|
|
56
95
|
|
|
57
96
|
class ItemPricingSet(BaseModel):
|
|
58
97
|
price_type: str
|
|
@@ -188,7 +227,7 @@ class ItemMerged(BaseModel):
|
|
|
188
227
|
source_entity_ids: list[str]
|
|
189
228
|
|
|
190
229
|
|
|
191
|
-
class ItemPatched(
|
|
230
|
+
class ItemPatched(_SkuGuard):
|
|
192
231
|
"""CSV upsert patch: accepts any item data fields."""
|
|
193
232
|
model_config = {"extra": "allow"}
|
|
194
233
|
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Copyright (c) 2026 Noah Severs
|
|
2
|
+
# SPDX-License-Identifier: BUSL-1.1
|
|
3
|
+
|
|
4
|
+
"""Canonical inventory code limits and validation - one source of truth.
|
|
5
|
+
|
|
6
|
+
The SKU/barcode invariants live here so the event boundary (celerp.events.schemas),
|
|
7
|
+
the interactive inventory routes, the allocation service, and the scanner all share
|
|
8
|
+
the exact same rules. A comma is Celerp's OR operator in the SKU/search syntax, so a
|
|
9
|
+
SKU may never contain one (it would split into separate codes wherever SKUs are
|
|
10
|
+
matched). A barcode is a numeric physical-lot identifier and must be unique per
|
|
11
|
+
company - enforced at the event boundary (format) and by a partial unique index on
|
|
12
|
+
the projections table (uniqueness).
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
MAX_BARCODE_LEN = 64
|
|
20
|
+
MAX_SKU_LEN = 255
|
|
21
|
+
MAX_SCAN_CODE_LEN = max(MAX_BARCODE_LEN, MAX_SKU_LEN)
|
|
22
|
+
|
|
23
|
+
# Partial unique index enforcing at-most-one item per (company, barcode). Declared on
|
|
24
|
+
# the Projection model so create_all builds it for the test schema, and created on
|
|
25
|
+
# existing databases by the barcode-uniqueness migration. Named here so the applier
|
|
26
|
+
# can tell a barcode violation apart from the (company_id, entity_id) primary-key race.
|
|
27
|
+
BARCODE_UNIQUE_INDEX = "uq_projection_company_item_barcode"
|
|
28
|
+
|
|
29
|
+
SKU_COMMA_MESSAGE = "SKU cannot contain a comma"
|
|
30
|
+
SKU_TOO_LONG_MESSAGE = f"SKU cannot exceed {MAX_SKU_LEN} characters"
|
|
31
|
+
BARCODE_NOT_DIGITS_MESSAGE = "Barcode must contain only digits"
|
|
32
|
+
BARCODE_TOO_LONG_MESSAGE = f"Barcode cannot exceed {MAX_BARCODE_LEN} characters"
|
|
33
|
+
BARCODE_CONFLICT_MESSAGE = "Barcode '{barcode}' already exists"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def reject_comma_sku(sku: Any) -> Any:
|
|
37
|
+
"""Reject a comma-bearing SKU. Returns the sku for chaining."""
|
|
38
|
+
if sku is not None and "," in str(sku):
|
|
39
|
+
raise ValueError(SKU_COMMA_MESSAGE)
|
|
40
|
+
return sku
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def validate_sku(sku: Any) -> Any:
|
|
44
|
+
"""Reject a comma-bearing or over-long SKU. Returns the sku for chaining."""
|
|
45
|
+
reject_comma_sku(sku)
|
|
46
|
+
if sku is not None and len(str(sku)) > MAX_SKU_LEN:
|
|
47
|
+
raise ValueError(SKU_TOO_LONG_MESSAGE)
|
|
48
|
+
return sku
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def validate_barcode(barcode: Any) -> Any:
|
|
52
|
+
"""Reject a non-digit or over-long barcode. An absent/empty barcode is allowed."""
|
|
53
|
+
if barcode is None:
|
|
54
|
+
return barcode
|
|
55
|
+
s = str(barcode)
|
|
56
|
+
if s == "":
|
|
57
|
+
return barcode
|
|
58
|
+
if not s.isdigit():
|
|
59
|
+
raise ValueError(BARCODE_NOT_DIGITS_MESSAGE)
|
|
60
|
+
if len(s) > MAX_BARCODE_LEN:
|
|
61
|
+
raise ValueError(BARCODE_TOO_LONG_MESSAGE)
|
|
62
|
+
return barcode
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class BarcodeConflictError(Exception):
|
|
66
|
+
"""A write violated the (company, item, barcode) uniqueness invariant.
|
|
67
|
+
|
|
68
|
+
Raised by the projection applier when the DB unique index rejects an insert - the
|
|
69
|
+
final defense for any writer that bypasses the application lock (imports,
|
|
70
|
+
connectors, a future writer). The API layer maps it to 409.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(self, barcode: str | None = None):
|
|
74
|
+
self.barcode = barcode
|
|
75
|
+
super().__init__(
|
|
76
|
+
BARCODE_CONFLICT_MESSAGE.format(barcode=barcode) if barcode else "Barcode already exists"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def is_barcode_unique_violation(exc: Exception) -> bool:
|
|
81
|
+
"""True when an IntegrityError is the barcode unique-index violation (not the PK race)."""
|
|
82
|
+
orig = getattr(exc, "orig", None)
|
|
83
|
+
if getattr(orig, "constraint_name", None) == BARCODE_UNIQUE_INDEX:
|
|
84
|
+
return True
|
|
85
|
+
# Expression-index violations do not always populate constraint_name across drivers;
|
|
86
|
+
# the index name still appears in the server message.
|
|
87
|
+
return BARCODE_UNIQUE_INDEX in str(exc)
|
|
@@ -13,6 +13,7 @@ from slowapi.errors import RateLimitExceeded
|
|
|
13
13
|
from slowapi.util import get_remote_address
|
|
14
14
|
|
|
15
15
|
from celerp.db import engine, mask_db_credentials
|
|
16
|
+
from celerp.inventory_codes import BarcodeConflictError
|
|
16
17
|
from celerp.config import settings, assert_secure_jwt, ensure_instance_id, load_cloud_config, load_backup_config
|
|
17
18
|
load_cloud_config()
|
|
18
19
|
load_backup_config()
|
|
@@ -447,6 +448,14 @@ async def rate_limit_handler(_request: Request, _exc: RateLimitExceeded):
|
|
|
447
448
|
return JSONResponse(status_code=429, content={"detail": "Rate limit exceeded"})
|
|
448
449
|
|
|
449
450
|
|
|
451
|
+
@app.exception_handler(BarcodeConflictError)
|
|
452
|
+
async def barcode_conflict_handler(_request: Request, exc: BarcodeConflictError):
|
|
453
|
+
# The projection applier raises this when the barcode unique index rejects a write
|
|
454
|
+
# that bypassed the allocation lock (imports, connectors). A more specific handler
|
|
455
|
+
# than the Exception catch-all, so it maps to 409 instead of a masked 500.
|
|
456
|
+
return JSONResponse(status_code=409, content={"detail": str(exc)})
|
|
457
|
+
|
|
458
|
+
|
|
450
459
|
# Kernel routes — always present regardless of module configuration
|
|
451
460
|
app.include_router(health.router, tags=["system"])
|
|
452
461
|
app.include_router(auth.router, prefix="/auth", tags=["auth"])
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Copyright (c) 2026 Noah Severs
|
|
2
|
+
# SPDX-License-Identifier: BUSL-1.1
|
|
3
|
+
|
|
4
|
+
"""Enforce at-most-one item per (company, barcode) with a partial unique index.
|
|
5
|
+
|
|
6
|
+
Revision ID: bc0d1e2f3a4b
|
|
7
|
+
Revises: b3c4d5e6f7a8
|
|
8
|
+
Create Date: 2026-08-25
|
|
9
|
+
|
|
10
|
+
Context
|
|
11
|
+
-------
|
|
12
|
+
A barcode identifies at most one physical-lot item per company. Until now that was
|
|
13
|
+
only enforced by an application check under a lock; any writer bypassing the lock
|
|
14
|
+
(imports, connectors, a future writer) could create duplicates, and legacy databases
|
|
15
|
+
may already hold some.
|
|
16
|
+
|
|
17
|
+
This migration is the final defense: a PostgreSQL partial expression unique index on
|
|
18
|
+
``(company_id, state ->> 'barcode')`` for item projections with a non-empty barcode.
|
|
19
|
+
|
|
20
|
+
It refuses to create the index while incompatible data exists rather than silently
|
|
21
|
+
renaming or clearing anything. A preflight scans for duplicate non-empty barcodes,
|
|
22
|
+
barcodes over the length limit, SKUs over the length limit, and comma-bearing SKUs,
|
|
23
|
+
and stops with an actionable report (company, value, affected entity ids) so an
|
|
24
|
+
operator can remediate and re-run.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import sqlalchemy as sa
|
|
30
|
+
from alembic import op
|
|
31
|
+
|
|
32
|
+
from celerp.inventory_codes import BARCODE_UNIQUE_INDEX, MAX_BARCODE_LEN, MAX_SKU_LEN
|
|
33
|
+
|
|
34
|
+
revision = "bc0d1e2f3a4b"
|
|
35
|
+
down_revision = "b3c4d5e6f7a8"
|
|
36
|
+
branch_labels = None
|
|
37
|
+
depends_on = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _preflight(conn) -> list[str]:
|
|
41
|
+
"""Return a list of human-readable remediation problems; empty means clean."""
|
|
42
|
+
problems: list[str] = []
|
|
43
|
+
|
|
44
|
+
dups = conn.execute(
|
|
45
|
+
sa.text(
|
|
46
|
+
"SELECT company_id, state ->> 'barcode' AS barcode, "
|
|
47
|
+
" array_agg(entity_id ORDER BY entity_id) AS entities "
|
|
48
|
+
"FROM projections "
|
|
49
|
+
"WHERE entity_type = 'item' AND NULLIF(state ->> 'barcode', '') IS NOT NULL "
|
|
50
|
+
"GROUP BY company_id, state ->> 'barcode' "
|
|
51
|
+
"HAVING count(*) > 1"
|
|
52
|
+
)
|
|
53
|
+
).fetchall()
|
|
54
|
+
for row in dups:
|
|
55
|
+
problems.append(
|
|
56
|
+
f"duplicate barcode {row.barcode!r} in company {row.company_id}: entities {list(row.entities)}"
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
long_barcodes = conn.execute(
|
|
60
|
+
sa.text(
|
|
61
|
+
"SELECT company_id, entity_id, state ->> 'barcode' AS barcode "
|
|
62
|
+
"FROM projections "
|
|
63
|
+
"WHERE entity_type = 'item' AND length(state ->> 'barcode') > :n"
|
|
64
|
+
),
|
|
65
|
+
{"n": MAX_BARCODE_LEN},
|
|
66
|
+
).fetchall()
|
|
67
|
+
for row in long_barcodes:
|
|
68
|
+
problems.append(
|
|
69
|
+
f"barcode {row.barcode!r} over {MAX_BARCODE_LEN} chars in company {row.company_id} entity {row.entity_id}"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
long_skus = conn.execute(
|
|
73
|
+
sa.text(
|
|
74
|
+
"SELECT company_id, entity_id "
|
|
75
|
+
"FROM projections "
|
|
76
|
+
"WHERE entity_type = 'item' AND length(state ->> 'sku') > :n"
|
|
77
|
+
),
|
|
78
|
+
{"n": MAX_SKU_LEN},
|
|
79
|
+
).fetchall()
|
|
80
|
+
for row in long_skus:
|
|
81
|
+
problems.append(
|
|
82
|
+
f"SKU over {MAX_SKU_LEN} chars in company {row.company_id} entity {row.entity_id}"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
comma_skus = conn.execute(
|
|
86
|
+
sa.text(
|
|
87
|
+
"SELECT company_id, entity_id, state ->> 'sku' AS sku "
|
|
88
|
+
"FROM projections "
|
|
89
|
+
"WHERE entity_type = 'item' AND position(',' in state ->> 'sku') > 0"
|
|
90
|
+
)
|
|
91
|
+
).fetchall()
|
|
92
|
+
for row in comma_skus:
|
|
93
|
+
problems.append(
|
|
94
|
+
f"comma-bearing SKU {row.sku!r} in company {row.company_id} entity {row.entity_id}"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return problems
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def upgrade() -> None:
|
|
101
|
+
conn = op.get_bind()
|
|
102
|
+
|
|
103
|
+
problems = _preflight(conn)
|
|
104
|
+
if problems:
|
|
105
|
+
raise RuntimeError(
|
|
106
|
+
"Cannot create the barcode uniqueness index: incompatible item data exists. "
|
|
107
|
+
"Resolve each of the following (rename or clear the barcode/SKU on the affected "
|
|
108
|
+
"items), then re-run the migration:\n - " + "\n - ".join(problems)
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
op.execute(
|
|
112
|
+
f"CREATE UNIQUE INDEX IF NOT EXISTS {BARCODE_UNIQUE_INDEX} "
|
|
113
|
+
"ON projections (company_id, (state ->> 'barcode')) "
|
|
114
|
+
"WHERE entity_type = 'item' AND NULLIF(state ->> 'barcode', '') IS NOT NULL"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def downgrade() -> None:
|
|
119
|
+
op.execute(f"DROP INDEX IF EXISTS {BARCODE_UNIQUE_INDEX}")
|
|
@@ -9,12 +9,27 @@ import sqlalchemy as sa
|
|
|
9
9
|
from sqlalchemy import BigInteger, Boolean, DateTime, ForeignKey, PrimaryKeyConstraint, String, Text
|
|
10
10
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
11
11
|
|
|
12
|
+
from celerp.inventory_codes import BARCODE_UNIQUE_INDEX
|
|
12
13
|
from celerp.models.base import Base
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class Projection(Base):
|
|
16
17
|
__tablename__ = "projections"
|
|
17
|
-
__table_args__ = (
|
|
18
|
+
__table_args__ = (
|
|
19
|
+
PrimaryKeyConstraint("company_id", "entity_id"),
|
|
20
|
+
# At most one item per (company, non-empty barcode). The final defense behind
|
|
21
|
+
# the application allocation lock: imports, connectors, or a future writer that
|
|
22
|
+
# bypasses the lock still cannot create a duplicate barcode. Declared here so
|
|
23
|
+
# create_all builds it for the test schema; created on existing databases by the
|
|
24
|
+
# barcode-uniqueness migration.
|
|
25
|
+
sa.Index(
|
|
26
|
+
BARCODE_UNIQUE_INDEX,
|
|
27
|
+
"company_id",
|
|
28
|
+
sa.text("(state ->> 'barcode')"),
|
|
29
|
+
unique=True,
|
|
30
|
+
postgresql_where=sa.text("entity_type = 'item' AND NULLIF(state ->> 'barcode', '') IS NOT NULL"),
|
|
31
|
+
),
|
|
32
|
+
)
|
|
18
33
|
|
|
19
34
|
entity_id: Mapped[str] = mapped_column(Text, nullable=False)
|
|
20
35
|
company_id: Mapped[uuid.UUID] = mapped_column(sa.Uuid(as_uuid=True), ForeignKey("companies.id"), nullable=False)
|
|
@@ -10,6 +10,7 @@ from datetime import datetime, timezone
|
|
|
10
10
|
from sqlalchemy import delete, select
|
|
11
11
|
from sqlalchemy.exc import IntegrityError
|
|
12
12
|
|
|
13
|
+
from celerp.inventory_codes import BarcodeConflictError, is_barcode_unique_violation
|
|
13
14
|
from celerp.models.ledger import LedgerEntry
|
|
14
15
|
from celerp.models.projections import Projection
|
|
15
16
|
|
|
@@ -130,10 +131,33 @@ class ProjectionEngine:
|
|
|
130
131
|
)
|
|
131
132
|
await session.flush()
|
|
132
133
|
return
|
|
133
|
-
except IntegrityError:
|
|
134
|
+
except IntegrityError as exc:
|
|
135
|
+
# Only the (company_id, entity_id) primary-key race is a benign
|
|
136
|
+
# concurrent-first-insert to retry as an update on the winner's row.
|
|
137
|
+
# A barcode unique-index violation is a real conflict - surface it so
|
|
138
|
+
# the API maps it to 409 rather than swallowing it as a PK race.
|
|
139
|
+
if is_barcode_unique_violation(exc):
|
|
140
|
+
raise BarcodeConflictError((fields.get("state") or {}).get("barcode")) from exc
|
|
141
|
+
constraint = getattr(getattr(exc, "orig", None), "constraint_name", None)
|
|
142
|
+
if constraint not in (None, "projections_pkey"):
|
|
143
|
+
raise
|
|
134
144
|
projection = await ProjectionEngine._locked_projection(session, entry)
|
|
135
|
-
|
|
145
|
+
if projection is None:
|
|
146
|
+
raise
|
|
147
|
+
fields = ProjectionEngine._next_fields(projection.state, entry, projection.version)
|
|
148
|
+
for column, value in fields.items():
|
|
136
149
|
setattr(projection, column, value)
|
|
150
|
+
# Flush inside a SAVEPOINT so a barcode unique-index violation on an UPDATE
|
|
151
|
+
# (not only a first insert) surfaces as BarcodeConflictError -> 409 instead
|
|
152
|
+
# of escaping to the outer commit masked as a 500. Unrelated integrity errors
|
|
153
|
+
# re-raise unchanged.
|
|
154
|
+
try:
|
|
155
|
+
async with session.begin_nested():
|
|
156
|
+
await session.flush()
|
|
157
|
+
except IntegrityError as exc:
|
|
158
|
+
if is_barcode_unique_violation(exc):
|
|
159
|
+
raise BarcodeConflictError((fields.get("state") or {}).get("barcode")) from exc
|
|
160
|
+
raise
|
|
137
161
|
|
|
138
162
|
@staticmethod
|
|
139
163
|
async def rebuild(session, company_id=None) -> None:
|
|
@@ -44,42 +44,74 @@ _DEFAULT_PRICE_LISTS: list[dict] = [
|
|
|
44
44
|
|
|
45
45
|
# Base schema fields (without price columns - those are injected dynamically)
|
|
46
46
|
_BASE_FIELDS: list[dict] = [
|
|
47
|
-
{"key": "sku", "label": "SKU", "type": "text", "editable": True, "required": True, "options": [], "visible_to_roles": [], "position": 0, "show_in_table": True},
|
|
48
|
-
{"key": "name", "label": "Name", "type": "text", "editable": True, "required": True, "options": [], "visible_to_roles": [], "position": 1, "show_in_table": True},
|
|
49
|
-
{"key": "category", "label": "Category", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 2, "show_in_table": True},
|
|
50
|
-
{"key": "quantity", "label": "Qty", "type": "number", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 3, "show_in_table": True, "tooltip_key": "field.tooltip.quantity"},
|
|
51
|
-
{"key": "sell_by", "label": "Sell Unit", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 3.05,"show_in_table": False, "tooltip_key": "field.tooltip.sell_by"},
|
|
52
|
-
{"key": "weight", "label": "Net Weight", "type": "number", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 3.02, "show_in_table": True, "tooltip_key": "field.tooltip.weight"},
|
|
53
|
-
{"key": "weight_unit", "label": "Net Weight Unit", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 3.15,"show_in_table": False, "tooltip_key": "field.tooltip.weight_unit"},
|
|
54
|
-
{"key": "pieces", "label": "Pieces", "type": "number", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 3.06,"show_in_table": True, "tooltip_key": "field.tooltip.pieces"},
|
|
55
|
-
{"key": "reorder_point", "label": "Reorder Point", "type": "number", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 3.7, "show_in_table": False, "tooltip_key": "field.tooltip.reorder_point"},
|
|
56
|
-
{"key": "reorder_qty", "label": "Reorder Qty", "type": "number", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 3.72,"show_in_table": False, "tooltip_key": "field.tooltip.reorder_qty"},
|
|
57
|
-
{"key": "gross_weight", "label": "Gross Weight", "type": "number", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 3.25,"show_in_table": False, "tooltip_key": "field.tooltip.gross_weight"},
|
|
58
|
-
{"key": "gross_weight_unit", "label": "Gross Weight Unit", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 3.3, "show_in_table": False, "tooltip_key": "field.tooltip.gross_weight_unit"},
|
|
59
|
-
{"key": "inventory_type", "label": "Inventory Type", "type": "select", "editable": True, "required": False, "options": ["stocked", "component", "non_stocked", "service", "freight"], "visible_to_roles": [], "position": 4.2, "show_in_table": False, "tooltip_key": "field.tooltip.inventory_type"},
|
|
60
|
-
{"key": "allow_splitting", "label": "Allow Splitting", "type": "bool", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 4.5, "show_in_table": False, "tooltip_key": "field.tooltip.allow_splitting"},
|
|
61
|
-
{"key": "pick_method", "label": "Stock Cutting", "type": "select", "editable": True, "required": False, "options": ["default", "fifo", "fefo", "lifo"], "visible_to_roles": [], "position": 4.6, "show_in_table": False, "tooltip_key": "field.tooltip.pick_method"},
|
|
62
|
-
{"key": "location_name", "label": "Location", "type": "text", "editable": False, "required": False, "options": [], "visible_to_roles": [], "position": 5, "show_in_table": True, "tooltip_key": "field.tooltip.location_name"},
|
|
47
|
+
{"key": "sku", "label": "SKU", "type": "text", "editable": True, "required": True, "options": [], "visible_to_roles": [], "position": 0, "show_in_table": True, "label_key": "field.label.sku"},
|
|
48
|
+
{"key": "name", "label": "Name", "type": "text", "editable": True, "required": True, "options": [], "visible_to_roles": [], "position": 1, "show_in_table": True, "label_key": "field.label.name"},
|
|
49
|
+
{"key": "category", "label": "Category", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 2, "show_in_table": True, "label_key": "field.label.category"},
|
|
50
|
+
{"key": "quantity", "label": "Qty", "type": "number", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 3, "show_in_table": True, "tooltip_key": "field.tooltip.quantity", "label_key": "field.label.quantity"},
|
|
51
|
+
{"key": "sell_by", "label": "Sell Unit", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 3.05,"show_in_table": False, "tooltip_key": "field.tooltip.sell_by", "label_key": "field.label.sell_by"},
|
|
52
|
+
{"key": "weight", "label": "Net Weight", "type": "number", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 3.02, "show_in_table": True, "tooltip_key": "field.tooltip.weight", "label_key": "field.label.weight"},
|
|
53
|
+
{"key": "weight_unit", "label": "Net Weight Unit", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 3.15,"show_in_table": False, "tooltip_key": "field.tooltip.weight_unit", "label_key": "field.label.weight_unit"},
|
|
54
|
+
{"key": "pieces", "label": "Pieces", "type": "number", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 3.06,"show_in_table": True, "tooltip_key": "field.tooltip.pieces", "label_key": "field.label.pieces"},
|
|
55
|
+
{"key": "reorder_point", "label": "Reorder Point", "type": "number", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 3.7, "show_in_table": False, "tooltip_key": "field.tooltip.reorder_point", "label_key": "field.label.reorder_point"},
|
|
56
|
+
{"key": "reorder_qty", "label": "Reorder Qty", "type": "number", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 3.72,"show_in_table": False, "tooltip_key": "field.tooltip.reorder_qty", "label_key": "field.label.reorder_qty"},
|
|
57
|
+
{"key": "gross_weight", "label": "Gross Weight", "type": "number", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 3.25,"show_in_table": False, "tooltip_key": "field.tooltip.gross_weight", "label_key": "field.label.gross_weight"},
|
|
58
|
+
{"key": "gross_weight_unit", "label": "Gross Weight Unit", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 3.3, "show_in_table": False, "tooltip_key": "field.tooltip.gross_weight_unit", "label_key": "field.label.gross_weight_unit"},
|
|
59
|
+
{"key": "inventory_type", "label": "Inventory Type", "type": "select", "editable": True, "required": False, "options": ["stocked", "component", "non_stocked", "service", "freight"], "visible_to_roles": [], "position": 4.2, "show_in_table": False, "tooltip_key": "field.tooltip.inventory_type", "label_key": "field.label.inventory_type"},
|
|
60
|
+
{"key": "allow_splitting", "label": "Allow Splitting", "type": "bool", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 4.5, "show_in_table": False, "tooltip_key": "field.tooltip.allow_splitting", "label_key": "field.label.allow_splitting"},
|
|
61
|
+
{"key": "pick_method", "label": "Stock Cutting", "type": "select", "editable": True, "required": False, "options": ["default", "fifo", "fefo", "lifo"], "visible_to_roles": [], "position": 4.6, "show_in_table": False, "tooltip_key": "field.tooltip.pick_method", "label_key": "field.label.pick_method"},
|
|
62
|
+
{"key": "location_name", "label": "Location", "type": "text", "editable": False, "required": False, "options": [], "visible_to_roles": [], "position": 5, "show_in_table": True, "tooltip_key": "field.tooltip.location_name", "label_key": "field.label.location_name"},
|
|
63
63
|
# Price columns are injected dynamically at position 6+ by _inject_price_columns()
|
|
64
64
|
# Read-only: status changes only through dedicated actions (Make Available, Revert
|
|
65
65
|
# to Draft), never a free-form dropdown edit - same convention as document status.
|
|
66
|
-
{"key": "status", "label": "Status", "type": "status", "editable": False, "required": False, "options": [], "visible_to_roles": [], "position": 100, "show_in_table": True},
|
|
67
|
-
{"key": "short_description", "label": "Short Description", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 101, "show_in_table": False},
|
|
68
|
-
{"key": "description", "label": "Description", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 102, "show_in_table": False},
|
|
69
|
-
{"key": "notes", "label": "Notes", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 103, "show_in_table": False},
|
|
70
|
-
{"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"},
|
|
71
|
-
{"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"},
|
|
72
|
-
{"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"},
|
|
73
|
-
{"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"},
|
|
74
|
-
{"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"},
|
|
75
|
-
{"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"},
|
|
76
|
-
{"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"},
|
|
77
|
-
{"key": "purchase_unit", "label": "Purchase Unit", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 104.8, "show_in_table": True, "tooltip_key": "field.tooltip.purchase_unit"},
|
|
78
|
-
{"key": "purchase_conversion_factor","label": "Conversion Factor", "type": "number", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 104.9, "show_in_table": False, "tooltip_key": "field.tooltip.purchase_conversion_factor"},
|
|
79
|
-
{"key": "created_at", "label": "Created", "type": "date", "editable": False, "required": False, "options": [], "visible_to_roles": [], "position": 107, "show_in_table": False},
|
|
80
|
-
{"key": "updated_at", "label": "Updated", "type": "date", "editable": False, "required": False, "options": [], "visible_to_roles": [], "position": 108, "show_in_table": False},
|
|
66
|
+
{"key": "status", "label": "Status", "type": "status", "editable": False, "required": False, "options": [], "visible_to_roles": [], "position": 100, "show_in_table": True, "label_key": "field.label.status"},
|
|
67
|
+
{"key": "short_description", "label": "Short Description", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 101, "show_in_table": False, "label_key": "field.label.short_description"},
|
|
68
|
+
{"key": "description", "label": "Description", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 102, "show_in_table": False, "label_key": "field.label.description"},
|
|
69
|
+
{"key": "notes", "label": "Notes", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 103, "show_in_table": False, "label_key": "field.label.notes"},
|
|
70
|
+
{"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", "label_key": "field.label.barcode"},
|
|
71
|
+
{"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", "label_key": "field.label.hs_code"},
|
|
72
|
+
{"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", "label_key": "field.label.country_of_origin"},
|
|
73
|
+
{"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", "label_key": "field.label.preferred_supplier"},
|
|
74
|
+
{"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", "label_key": "field.label.batch_no"},
|
|
75
|
+
{"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", "label_key": "field.label.purchase_sku"},
|
|
76
|
+
{"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", "label_key": "field.label.purchase_name"},
|
|
77
|
+
{"key": "purchase_unit", "label": "Purchase Unit", "type": "text", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 104.8, "show_in_table": True, "tooltip_key": "field.tooltip.purchase_unit", "label_key": "field.label.purchase_unit"},
|
|
78
|
+
{"key": "purchase_conversion_factor","label": "Conversion Factor", "type": "number", "editable": True, "required": False, "options": [], "visible_to_roles": [], "position": 104.9, "show_in_table": False, "tooltip_key": "field.tooltip.purchase_conversion_factor", "label_key": "field.label.purchase_conversion_factor"},
|
|
79
|
+
{"key": "created_at", "label": "Created", "type": "date", "editable": False, "required": False, "options": [], "visible_to_roles": [], "position": 107, "show_in_table": False, "label_key": "field.label.created_at"},
|
|
80
|
+
{"key": "updated_at", "label": "Updated", "type": "date", "editable": False, "required": False, "options": [], "visible_to_roles": [], "position": 108, "show_in_table": False, "label_key": "field.label.updated_at"},
|
|
81
81
|
]
|
|
82
82
|
|
|
83
|
+
# Built-in fields keyed for O(1) lookup when rehydrating stripped metadata.
|
|
84
|
+
_BASE_FIELD_BY_KEY: dict[str, dict] = {f["key"]: f for f in _BASE_FIELDS}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _rehydrate_builtin_metadata(schema: list[dict]) -> list[dict]:
|
|
88
|
+
"""Reattach canonical label_key/tooltip_key to built-in fields.
|
|
89
|
+
|
|
90
|
+
The item schema PATCH model does not carry label_key or tooltip_key, so a
|
|
91
|
+
company that customizes its schema stores built-in fields stripped of both.
|
|
92
|
+
Reattach them from _BASE_FIELDS, keyed strictly on built-in membership so
|
|
93
|
+
custom fields and dynamic price columns are never touched. label_key is
|
|
94
|
+
restored only when the stored label still equals the canonical English label
|
|
95
|
+
(a user-renamed built-in keeps its literal label and is never translated);
|
|
96
|
+
tooltip_key is restored for every built-in key, since there is no
|
|
97
|
+
custom-tooltip edit path. Idempotent: a field that already carries a key is
|
|
98
|
+
left as-is."""
|
|
99
|
+
out: list[dict] = []
|
|
100
|
+
for f in schema:
|
|
101
|
+
base = _BASE_FIELD_BY_KEY.get(f.get("key"))
|
|
102
|
+
if base is None:
|
|
103
|
+
out.append(f)
|
|
104
|
+
continue
|
|
105
|
+
updated = dict(f)
|
|
106
|
+
if ("label_key" not in updated and base.get("label_key")
|
|
107
|
+
and updated.get("label") == base.get("label")):
|
|
108
|
+
updated["label_key"] = base["label_key"]
|
|
109
|
+
if "tooltip_key" not in updated and base.get("tooltip_key"):
|
|
110
|
+
updated["tooltip_key"] = base["tooltip_key"]
|
|
111
|
+
out.append(updated)
|
|
112
|
+
return out
|
|
113
|
+
|
|
114
|
+
|
|
83
115
|
def _inject_price_columns(base: list[dict], price_lists: list[dict]) -> list[dict]:
|
|
84
116
|
"""Insert a money column for each price list after position 5 (location).
|
|
85
117
|
|
|
@@ -190,5 +222,5 @@ async def get_effective_field_schema(
|
|
|
190
222
|
keys_in_cat = {f["key"] for f in cat_fields}
|
|
191
223
|
merged = [f for f in base_schema if f["key"] not in keys_in_cat]
|
|
192
224
|
merged.extend(cat_fields)
|
|
193
|
-
return merged
|
|
194
|
-
return base_schema
|
|
225
|
+
return _rehydrate_builtin_metadata(merged)
|
|
226
|
+
return _rehydrate_builtin_metadata(base_schema)
|
|
@@ -10,6 +10,7 @@ and by the fulfillment module's toggle/pick screen.
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
import uuid as _uuid
|
|
13
|
+
from collections.abc import Awaitable, Callable
|
|
13
14
|
from datetime import datetime, timezone
|
|
14
15
|
from typing import Any
|
|
15
16
|
|
|
@@ -42,16 +43,36 @@ async def execute_fulfill(
|
|
|
42
43
|
company_id,
|
|
43
44
|
user_id,
|
|
44
45
|
doc_type: str = "",
|
|
46
|
+
allocate_barcodes: Callable[[int], Awaitable[list[str]]] | None = None,
|
|
45
47
|
) -> dict[str, Any]:
|
|
46
48
|
"""Execute fulfillment: emit item events, doc event, and COGS JE.
|
|
47
49
|
|
|
48
50
|
Returns: {fulfillment_status, fulfilled_items, total_cogs}
|
|
51
|
+
|
|
52
|
+
A split pick carves a new child parcel off its parent lot; that child is a
|
|
53
|
+
fresh physical item and needs its own barcode. Core must not depend on the
|
|
54
|
+
inventory module, so the calling module injects allocation through
|
|
55
|
+
``allocate_barcodes(count) -> list[str]`` (celerp_inventory allocates under
|
|
56
|
+
its code-namespace lock). When any split is planned and no allocator is
|
|
57
|
+
supplied, this fails before emitting a single event rather than minting a
|
|
58
|
+
barcodeless child.
|
|
49
59
|
"""
|
|
50
60
|
now = datetime.now(timezone.utc).isoformat()
|
|
51
61
|
fulfilled_items: list[dict] = []
|
|
52
62
|
total_cogs = 0.0
|
|
53
63
|
cid = _to_uuid(company_id)
|
|
54
64
|
uid = _to_uuid(user_id)
|
|
65
|
+
|
|
66
|
+
# Allocate every split child's barcode up front, under the caller's lock, and
|
|
67
|
+
# fail clearly before any event is emitted if the allocator is missing.
|
|
68
|
+
split_picks = [p for p in pick_result.picks if p.action == "split"]
|
|
69
|
+
if split_picks and allocate_barcodes is None:
|
|
70
|
+
raise ValueError(
|
|
71
|
+
"Fulfillment must split a lot into a new child parcel that needs a "
|
|
72
|
+
"freshly allocated barcode, but no allocate_barcodes callback was "
|
|
73
|
+
"supplied. The calling module must inject inventory code allocation."
|
|
74
|
+
)
|
|
75
|
+
split_barcodes = iter(await allocate_barcodes(len(split_picks))) if split_picks else iter(())
|
|
55
76
|
# Stable human doc number for the item's history (the entity_id suffix can diverge on
|
|
56
77
|
# finalize/renumber, e.g. PF-... -> INV-...), so capture it from the doc at emit time.
|
|
57
78
|
doc_number = doc_state.get("doc_number") or doc_state.get("ref_id") or ""
|
|
@@ -96,6 +117,7 @@ async def execute_fulfill(
|
|
|
96
117
|
"sku": pick.sku, # child keeps the parent SKU (distinct lot by entity_id)
|
|
97
118
|
"name": pick.sku,
|
|
98
119
|
"quantity": pick.pick_qty,
|
|
120
|
+
"barcode": next(split_barcodes), # fresh per-lot barcode from the injected allocator
|
|
99
121
|
},
|
|
100
122
|
actor_id=uid,
|
|
101
123
|
location_id=None,
|
|
@@ -198,6 +198,22 @@ TAX_REGIMES: dict[str, _T] = {
|
|
|
198
198
|
"description": "Tax-exempt", "is_compound": False, "default_order": 0},
|
|
199
199
|
],
|
|
200
200
|
},
|
|
201
|
+
# ── Africa ────────────────────────────────────────────────────────────
|
|
202
|
+
"ET": {
|
|
203
|
+
"currency": "ETB",
|
|
204
|
+
"taxes": [
|
|
205
|
+
{"name": "VAT 15%", "rate": 15.0, "tax_type": "both", "is_default": True,
|
|
206
|
+
"description": "Ethiopia standard VAT", "is_compound": False, "default_order": 0},
|
|
207
|
+
{"name": "TOT 2%", "rate": 2.0, "tax_type": "sales", "is_default": False,
|
|
208
|
+
"description": "Turnover tax on goods (non-VAT-registered)", "is_compound": False, "default_order": 0},
|
|
209
|
+
{"name": "TOT 10%", "rate": 10.0, "tax_type": "sales", "is_default": False,
|
|
210
|
+
"description": "Turnover tax on services (non-VAT-registered)", "is_compound": False, "default_order": 0},
|
|
211
|
+
{"name": "Exempt", "rate": 0.0, "tax_type": "both", "is_default": False,
|
|
212
|
+
"description": "VAT-exempt", "is_compound": False, "default_order": 0},
|
|
213
|
+
{"name": "WHT 2%", "rate": -2.0, "tax_type": "purchase", "is_default": False,
|
|
214
|
+
"description": "Withholding tax on supply of goods", "is_compound": False, "default_order": 1},
|
|
215
|
+
],
|
|
216
|
+
},
|
|
201
217
|
# ── Fallback ──────────────────────────────────────────────────────────
|
|
202
218
|
"_default": {
|
|
203
219
|
"currency": "USD",
|