diff --git a/.github/workflows/migration_tests.yaml b/.github/workflows/migration_tests.yaml index 1448133170..5a03a5aa46 100644 --- a/.github/workflows/migration_tests.yaml +++ b/.github/workflows/migration_tests.yaml @@ -50,7 +50,7 @@ jobs: contains(github.event.pull_request.labels.*.name, 'full-run') sqlite: - name: Tests - Migrations [SQLite] + name: Migrations [SQLite] runs-on: ubuntu-latest needs: paths-filter if: ${{ (needs.paths-filter.outputs.force == 'true') || (github.ref == 'refs/heads/master' && needs.paths-filter.outputs.migrations == 'true') }} @@ -83,7 +83,7 @@ jobs: flags: migrations-sqlite mysql: - name: Tests - Migrations [MySQL] + name: Migrations [MySQL] runs-on: ubuntu-24.04 needs: paths-filter if: ${{ (needs.paths-filter.outputs.force == 'true') || (github.ref == 'refs/heads/master' && needs.paths-filter.outputs.migrations == 'true') }} @@ -134,7 +134,7 @@ jobs: flags: migrations-mysql postgresql: - name: Tests - Migrations [PostgreSQL] + name: Migrations [PostgreSQL] runs-on: ubuntu-latest needs: paths-filter if: ${{ (needs.paths-filter.outputs.force == 'true') || (github.ref == 'refs/heads/master' && needs.paths-filter.outputs.migrations == 'true') }} @@ -181,7 +181,7 @@ jobs: flags: migrations-postgresql migrations-checks: - name: Tests - Database Migrations + name: Database Migrations runs-on: ubuntu-latest needs: paths-filter if: ${{ (needs.paths-filter.outputs.force == 'true') || (github.ref == 'refs/heads/master' && needs.paths-filter.outputs.migrations == 'true') }} @@ -205,37 +205,45 @@ jobs: - name: Fetch Database run: git clone --depth 1 https://github.com/inventree/test-db ./test-db - - name: 0.10.0 Database + - name: 1.0.0 Database + run: | + rm /home/runner/work/InvenTree/db.sqlite3 + cp test-db/stable_1.0.0.sqlite3 /home/runner/work/InvenTree/db.sqlite3 + chmod +rw /home/runner/work/InvenTree/db.sqlite3 + invoke migrate + + - name: 1.5.0 Database + run: | + rm /home/runner/work/InvenTree/db.sqlite3 + cp test-db/stable_1.5.0.sqlite3 /home/runner/work/InvenTree/db.sqlite3 + chmod +rw /home/runner/work/InvenTree/db.sqlite3 + invoke migrate + + - name: 0.10.0 Database (SHOULD FAIL) run: | rm /home/runner/work/InvenTree/db.sqlite3 cp test-db/stable_0.10.0.sqlite3 /home/runner/work/InvenTree/db.sqlite3 chmod +rw /home/runner/work/InvenTree/db.sqlite3 - invoke migrate - - name: 0.11.0 Database - run: | - rm /home/runner/work/InvenTree/db.sqlite3 - cp test-db/stable_0.11.0.sqlite3 /home/runner/work/InvenTree/db.sqlite3 - chmod +rw /home/runner/work/InvenTree/db.sqlite3 - invoke migrate + set +e + output=$(invoke migrate 2>&1) + exit_code=$? + set -e - - name: 0.13.5 Database - run: | - rm /home/runner/work/InvenTree/db.sqlite3 - cp test-db/stable_0.13.5.sqlite3 /home/runner/work/InvenTree/db.sqlite3 - chmod +rw /home/runner/work/InvenTree/db.sqlite3 - invoke migrate + # Always show the actual output, so the reason for pass/fail is visible + # in the job log rather than just a generic summary line + echo "$output" - - name: 0.16.0 Database - run: | - rm /home/runner/work/InvenTree/db.sqlite3 - cp test-db/stable_0.16.0.sqlite3 /home/runner/work/InvenTree/db.sqlite3 - chmod +rw /home/runner/work/InvenTree/db.sqlite3 - invoke migrate + if [ "$exit_code" -eq 0 ]; then + echo "::error::Migration succeeded for 0.10.0 database, but this is expected to fail (squashed migrations no longer support this old baseline)" + exit 1 + fi - - name: 0.17.0 Database - run: | - rm /home/runner/work/InvenTree/db.sqlite3 - cp test-db/stable_0.17.0.sqlite3 /home/runner/work/InvenTree/db.sqlite3 - chmod +rw /home/runner/work/InvenTree/db.sqlite3 - invoke migrate + # Confirm it failed for the *expected* reason (INVE-E19: stuck mid-way + # through the pre-1.0.0 migration squash), not some unrelated crash + if echo "$output" | grep -q "INVE-E19"; then + echo "Migration failed as expected for 0.10.0 database (INVE-E19)" + else + echo "::error::Migration failed for 0.10.0 database, but not with the expected INVE-E19 error - this looks like an unrelated failure" + exit 1 + fi diff --git a/CHANGELOG.md b/CHANGELOG.md index 0551bee7ef..d2c8e530ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Breaking Changes +- [#12830](https://github.com/inventree/InvenTree/pull/12830) squashes all database migrations prior to the 1.0.0 release. This means that any users who are updating from a version older than 1.0.0 must first update to the 1.0.0 release before updating to the current release. - [#11971](https://github.com/inventree/InvenTree/pull/11971) is a major refactor of how notes are handled. Notes are now stored in a separate database table (in line with how attachments are handled), and each model instance can have multiple notes associated with it. The `notes` field has been removed from the individual models (and their associated API endpoints), and notes are now accessed via the new `/api/note/` endpoint. Existing notes data (and any embedded images) are automatically migrated to the new notes table, with the markdown content converted to HTML. Any external client applications which read or write the `notes` field via the API will need to be updated to use the new endpoint. - [#12507](https://github.com/inventree/InvenTree/pull/12507) calling an invalid or repeated state transition now raises a ValidationError. Plugins implementing state transitions should evaluate the PR and adapt their usage of transitions to gain the new safeguards. - [#12672](https://github.com/inventree/InvenTree/pull/12672) renames the newly added `tags` filter from 1.4.0 (https://github.com/inventree/InvenTree/pull/12077) to `tag_name` to remove a nameclash. diff --git a/docs/docs/faq.md b/docs/docs/faq.md index cd2c7c3971..f00f98b32d 100644 --- a/docs/docs/faq.md +++ b/docs/docs/faq.md @@ -107,10 +107,14 @@ For more information, refer to the installation guides: - [Docker Installation](./start/docker_install.md#updating-inventree) - [Bare Metal Installation](./start/install.md#updating-inventree) +- [Package Installer](./start/installer.md#updating-inventree) !!! warning "Invoke Update" You must ensure that the `invoke update` command is performed *every time* you update InvenTree +!!! danger "Updating from Pre 1.0.0" + If your installation is running a version of InvenTree older than `1.0.0`, you cannot update directly to the current release - see [Updating from Pre 1.0.0](./start/migrate.md#updating-from-pre-100) for the required intermediate step. + ### Breaking Changes Before performing an update, check the release notes! Any *breaking changes* (changes which require user intervention) will be clearly noted. diff --git a/docs/docs/settings/error_codes.md b/docs/docs/settings/error_codes.md index bfd72f9399..e24b57e3fc 100644 --- a/docs/docs/settings/error_codes.md +++ b/docs/docs/settings/error_codes.md @@ -116,6 +116,11 @@ An error occurred while rendering a component in the frontend. Typically this is The InvenTree server failed to start, because the configured server URL does not include a top-level domain (TLD). A valid TLD is required for proper operation. +#### INVE-E19 +**Database stuck mid-way through pre-1.0.0 migration squash - Backend** + +If your installation is currently running a version of InvenTree older than `1.0.0`, you must first update to the `1.0.0` release before updating further. See [Updating from Pre 1.0.0](../start/migrate.md#updating-from-pre-100) for the full upgrade procedure. + ## Warning Codes ### INVE-W (InvenTree Warning) diff --git a/docs/docs/start/docker_install.md b/docs/docs/start/docker_install.md index 5e2388a889..91c9e19101 100644 --- a/docs/docs/start/docker_install.md +++ b/docs/docs/start/docker_install.md @@ -118,6 +118,9 @@ This command launches the following containers: To update your InvenTree installation to the latest version, follow these steps: +!!! danger "Updating from Pre 1.0.0" + If your installation is running a version of InvenTree older than `1.0.0`, you cannot update directly to the current release. See [Updating from Pre 1.0.0](./migrate.md#updating-from-pre-100) for the required intermediate step. + ### Stop Containers Stop all running containers as below: diff --git a/docs/docs/start/install.md b/docs/docs/start/install.md index b9276928c2..dade58355f 100644 --- a/docs/docs/start/install.md +++ b/docs/docs/start/install.md @@ -289,6 +289,9 @@ Administrators wishing to update InvenTree to the latest version should follow t !!! info "Update Database" It is advisable to [backup the InvenTree database](./backup.md) before performing these steps. The particular backup procedure may depend on your installation details. +!!! danger "Updating from Pre 1.0.0" + If your installation is running a version of InvenTree older than `1.0.0`, you cannot update directly to the current release. See [Updating from Pre 1.0.0](./migrate.md#updating-from-pre-100) for the required intermediate step. + ### Stop InvenTree Server Ensure the InvenTree server is stopped. This will depend on the particulars of your database installation. diff --git a/docs/docs/start/installer.md b/docs/docs/start/installer.md index 893a94730e..a66dc10d15 100644 --- a/docs/docs/start/installer.md +++ b/docs/docs/start/installer.md @@ -140,6 +140,9 @@ To change the data storage location, link the new location to `/opt/inventree/da ## Updating InvenTree +!!! danger "Updating from Pre 1.0.0" + If your installation is running a version of InvenTree older than `1.0.0`, you cannot update directly to the current release. See [Updating from Pre 1.0.0](./migrate.md#updating-from-pre-100) for the required intermediate step. + To update InvenTree run the following command, which updates the InvenTree package to the latest version: ```bash diff --git a/docs/docs/start/migrate.md b/docs/docs/start/migrate.md index 1b4a797986..e32226ac83 100644 --- a/docs/docs/start/migrate.md +++ b/docs/docs/start/migrate.md @@ -2,6 +2,24 @@ title: Migrating Data --- +## Updating from Pre 1.0.0 + +!!! danger "Required Stopover" + As part of the 1.0.0 release cycle, InvenTree's database migration history was *squashed* - many individual migration files were consolidated into a smaller number of squashed migrations, and the original (now-redundant) migration files were subsequently removed from the codebase. + + As a result, InvenTree cannot migrate a database directly from a version **older than 1.0.0** to the current release. Attempting to do so will cause `invoke update` (or `invoke migrate`) to fail. + +If your installation is currently running a version of InvenTree older than `1.0.0`, you must first update to the `1.0.0` release, before updating to the current release. + +### How to Update + +1. Determine your current InvenTree version. If it is older than `1.0.0`, do not skip directly to the latest release. +2. Follow the normal update procedure for your installation method - [Bare Metal](./install.md#updating-inventree), [Docker](./docker_install.md#updating-inventree), or [Package Installer](./installer.md#updating-inventree) - targeting a `1.0.0` release. +3. Once the database has been successfully updated to `1.0.0`, repeat the update procedure again to bring the installation up to the current release. + +!!! danger "Skipping Directly to Latest" + Attempting to update directly from a pre-1.0.0 database to the current release, skipping the `1.0.0` stopover, is not supported and will fail. + ## Migrating Data to a Different Database In the case that data needs to be migrated from one database installation to another, the following procedure can be used to export data, initialize the new database, and re-import the data. The following instructions apply to bare-metal and docker installations, although the particular commands required may vary slightly in each case. @@ -100,6 +118,9 @@ Copy the entire directory tree from the original InvenTree installation to the n If you are updating from an older version of InvenTree to a newer version, the migration steps outlined above *do not apply*. +!!! danger "Updating from Pre 1.0.0" + If your existing installation is running a version older than `1.0.0`, you cannot update directly to the current release. See [Updating from Pre 1.0.0](#updating-from-pre-100) above for the required intermediate step. + An update from an old version to a new one requires not only that the database *schema* are updated, but the *data* held within the database must be updated in the correct sequence. Follow the sequence of steps below to ensure that the database records are updated correctly. diff --git a/src/backend/InvenTree/InvenTree/apps.py b/src/backend/InvenTree/InvenTree/apps.py index d417002507..1a7687d0e4 100644 --- a/src/backend/InvenTree/InvenTree/apps.py +++ b/src/backend/InvenTree/InvenTree/apps.py @@ -22,6 +22,7 @@ from InvenTree.ready import ignore_ready_warning logger = structlog.get_logger('inventree') MIGRATIONS_CHECK_DONE = False +PRE_1_0_0_CHECK_DONE = False OIDC_CLIENT_CHECKED = False DEFAULT_OIDC_APP_ID = 'zDFnsiRheJIOKNx6aCQ0quBxECg1QBHtVFDPloJ6' @@ -55,6 +56,13 @@ class InvenTreeConfig(AppConfig): ): return + # Check for a database stuck mid-way through the pre-1.0.0 migration squash. + if ( + InvenTree.ready.canAppAccessDatabase(allow_plugins=True) + or settings.TESTING_ENV + ): + self.check_pre_1_0_0_upgrade() + # Skip if running migrations if InvenTree.ready.isRunningMigrations(): return @@ -370,6 +378,31 @@ class InvenTreeConfig(AppConfig): logger.info('Default OIDC client created: %s', client) OIDC_CLIENT_CHECKED = True + def check_pre_1_0_0_upgrade(self=None): + """Check for a database stuck mid-way through the pre-1.0.0 migration squash.""" + global PRE_1_0_0_CHECK_DONE + if PRE_1_0_0_CHECK_DONE: + return + + if not InvenTree.ready.canAppAccessDatabase(allow_plugins=True): + return + + if stuck_apps := InvenTree.tasks.get_stuck_pre_1_0_0_apps(): + docs = 'https://docs.inventree.org/en/stable/start/migrate/#updating-from-pre-100' + logger.error( + 'INVE-E19: Database is only partially migrated through a pre-1.0.0 ' + 'install, for app(s): %s\n' + 'This instance must first be fully upgraded to InvenTree 1.0.0 ' + 'before upgrading further.\n' + '- Refer to the InvenTree documentation for more information:\n' + '- %s', + ', '.join(stuck_apps), + docs, + ) + sys.exit(1) + + PRE_1_0_0_CHECK_DONE = True + def ensure_migrations_done(self=None): """Ensures there are no open migrations, stop if inconsistent state.""" global MIGRATIONS_CHECK_DONE diff --git a/src/backend/InvenTree/InvenTree/tasks.py b/src/backend/InvenTree/InvenTree/tasks.py index f16832c531..a0abe45be6 100644 --- a/src/backend/InvenTree/InvenTree/tasks.py +++ b/src/backend/InvenTree/InvenTree/tasks.py @@ -995,6 +995,63 @@ def get_migration_count(): return executor.loader.applied_migrations +# The first and last individual migrations of each pre-1.0.0 squash range, +# for every app squashed as part of the pre-1.0.0 migration-history cleanup. +PRE_1_0_0_MIGRATION_BOUNDARIES = [ + ('common', '0001_initial', '0007_colortheme'), + ( + 'common', + '0008_remove_inventreesetting_description', + '0039_emailthread_emailmessage', + ), + ('build', '0006_auto_20190913_1407', '0015_auto_20200425_1350'), + ('build', '0017_auto_20200426_0612', '0058_buildline_consumed'), + ('company', '0003_remove_supplierpart_minimum', '0047_supplierpart_pack_size'), + ('company', '0048_auto_20220913_0312', '0075_company_tax_id'), + ('order', '0001_initial', '0023_auto_20200420_2309'), + ('order', '0031_auto_20200426_0612', '0112_alter_salesorderlineitem_part'), + ('stock', '0002_auto_20190525_2226', '0030_auto_20200422_0015'), + ('stock', '0059_auto_20210404_2016', '0116_alter_stockitem_link'), + ('part', '0003_auto_20190525_2226', '0060_merge_20201112_1722'), + ( + 'part', + '0061_auto_20210103_2313', + '0142_remove_part_last_stocktake_remove_partstocktake_note_and_more', + ), + ('users', '0001_initial', '0015_alter_userprofile_type'), +] + + +def get_stuck_pre_1_0_0_apps() -> list: + """Detect apps stuck mid-way through the pre-1.0.0 migration squash. + + A database which has applied the *first* migration of one of + PRE_1_0_0_MIGRATION_BOUNDARIES's ranges but not the *last* is stuck + between the old, granular history and the squashed one. + + Returns a list of app labels which are in this "stuck" state. An empty + list means it is safe to proceed with migrations. + """ + from django.db.migrations.recorder import MigrationRecorder + + connection = connections[DEFAULT_DB_ALIAS] + recorder = MigrationRecorder(connection) + + if not recorder.has_table(): + # No migrations have ever been recorded - a genuinely fresh database + return [] + + applied = recorder.applied_migrations() + + stuck_apps = set() + + for app_label, first, last in PRE_1_0_0_MIGRATION_BOUNDARIES: + if (app_label, first) in applied and (app_label, last) not in applied: + stuck_apps.add(app_label) + + return sorted(stuck_apps) + + @tracer.start_as_current_span('check_for_migrations') @scheduled_task(ScheduledTask.DAILY) def check_for_migrations(force: bool = False, reload_registry: bool = True) -> bool: diff --git a/src/backend/InvenTree/build/migrations/0006_auto_20190913_1407.py b/src/backend/InvenTree/build/migrations/0006_auto_20190913_1407.py deleted file mode 100644 index 917d3a4e42..0000000000 --- a/src/backend/InvenTree/build/migrations/0006_auto_20190913_1407.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.5 on 2019-09-13 14:07 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0005_auto_20190604_2217'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='URL', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0006_auto_20190913_1407_squashed_0015_auto_20200425_1350.py b/src/backend/InvenTree/build/migrations/0006_auto_20190913_1407_squashed_0015_auto_20200425_1350.py new file mode 100644 index 0000000000..bc055e40b9 --- /dev/null +++ b/src/backend/InvenTree/build/migrations/0006_auto_20190913_1407_squashed_0015_auto_20200425_1350.py @@ -0,0 +1,197 @@ +# Generated by Django 5.2.17 on 2026-09-09 23:26 + +import InvenTree.fields +import django.core.validators +import django.db.models.deletion +import mptt.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + replaces = [ + ("build", "0006_auto_20190913_1407"), + ("build", "0007_auto_20191118_2321"), + ("build", "0008_auto_20200201_1247"), + ("build", "0009_auto_20200210_1032"), + ("build", "0010_auto_20200318_1027"), + ("build", "0011_auto_20200406_0123"), + ("build", "0012_build_sales_order"), + ("build", "0013_auto_20200425_0507"), + ("build", "0014_auto_20200425_1243"), + ("build", "0015_auto_20200425_1350"), + ] + + dependencies = [ + ("build", "0005_auto_20190604_2217"), + ("order", "0029_auto_20200423_1042"), + ("part", "0035_auto_20200406_0045"), + ("stock", "0031_auto_20200422_0209"), + ] + + operations = [ + migrations.AlterField( + model_name="build", + name="notes", + field=models.TextField( + blank=True, help_text="Extra build notes", verbose_name="Notes" + ), + ), + migrations.AlterField( + model_name="build", + name="creation_date", + field=models.DateField(auto_now_add=True), + ), + migrations.AlterField( + model_name="build", + name="part", + field=models.ForeignKey( + help_text="Select part to build", + limit_choices_to={ + "active": True, + "assembly": True, + "is_template": False, + "virtual": False, + }, + on_delete=django.db.models.deletion.CASCADE, + related_name="builds", + to="part.part", + verbose_name="Part", + ), + ), + migrations.RenameField( + model_name="build", + old_name="URL", + new_name="link", + ), + migrations.AlterField( + model_name="build", + name="link", + field=InvenTree.fields.InvenTreeURLField( + blank=True, + help_text="Link to external URL", + max_length=2000, + verbose_name="External Link", + ), + ), + migrations.AddField( + model_name="build", + name="sales_order", + field=models.ForeignKey( + blank=True, + help_text="Sales Order to which this build is allocated", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="builds", + to="order.salesorder", + verbose_name="Sales Order Reference", + ), + ), + migrations.AddField( + model_name="build", + name="level", + field=models.PositiveIntegerField(default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name="build", + name="lft", + field=models.PositiveIntegerField(default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name="build", + name="rght", + field=models.PositiveIntegerField(default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name="build", + name="tree_id", + field=models.PositiveIntegerField(db_index=True, default=0, editable=False), + preserve_default=False, + ), + migrations.AlterField( + model_name="build", + name="batch", + field=models.CharField( + blank=True, + help_text="Batch code for this build output", + max_length=100, + null=True, + verbose_name="Batch Code", + ), + ), + migrations.AlterField( + model_name="build", + name="quantity", + field=models.PositiveIntegerField( + default=1, + help_text="Number of parts to build", + validators=[django.core.validators.MinValueValidator(1)], + verbose_name="Build Quantity", + ), + ), + migrations.AlterField( + model_name="build", + name="status", + field=models.PositiveIntegerField( + choices=[ + (10, "Pending"), + (20, "Allocated"), + (30, "Cancelled"), + (40, "Complete"), + ], + default=10, + help_text="Build status code", + validators=[django.core.validators.MinValueValidator(0)], + verbose_name="Build Status", + ), + ), + migrations.AlterField( + model_name="build", + name="take_from", + field=models.ForeignKey( + blank=True, + help_text="Select location to take stock from for this build (leave blank to take from any stock location)", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="sourcing_builds", + to="stock.stocklocation", + verbose_name="Source Location", + ), + ), + migrations.AlterField( + model_name="build", + name="title", + field=models.CharField( + help_text="Brief description of the build", + max_length=100, + verbose_name="Build Title", + ), + ), + migrations.AddField( + model_name="build", + name="parent", + field=mptt.fields.TreeForeignKey( + blank=True, + help_text="Parent build to which this build is allocated", + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="children", + to="build.build", + verbose_name="Parent Build", + ), + ), + migrations.AlterField( + model_name="builditem", + name="quantity", + field=models.DecimalField( + decimal_places=5, + default=1, + help_text="Stock quantity to allocate to build", + max_digits=15, + validators=[django.core.validators.MinValueValidator(0)], + ), + ), + ] diff --git a/src/backend/InvenTree/build/migrations/0007_auto_20191118_2321.py b/src/backend/InvenTree/build/migrations/0007_auto_20191118_2321.py deleted file mode 100644 index da6b455f60..0000000000 --- a/src/backend/InvenTree/build/migrations/0007_auto_20191118_2321.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.5 on 2019-11-18 23:21 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0006_auto_20190913_1407'), - ] - - operations = [ - migrations.AlterField( - model_name='builditem', - name='quantity', - field=models.DecimalField(decimal_places=5, default=1, help_text='Stock quantity to allocate to build', max_digits=15, validators=[django.core.validators.MinValueValidator(1)]), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0008_auto_20200201_1247.py b/src/backend/InvenTree/build/migrations/0008_auto_20200201_1247.py deleted file mode 100644 index ff311ee184..0000000000 --- a/src/backend/InvenTree/build/migrations/0008_auto_20200201_1247.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.9 on 2020-02-01 12:47 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0007_auto_20191118_2321'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='notes', - field=models.TextField(blank=True, help_text='Extra build notes'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0009_auto_20200210_1032.py b/src/backend/InvenTree/build/migrations/0009_auto_20200210_1032.py deleted file mode 100644 index 45908933b9..0000000000 --- a/src/backend/InvenTree/build/migrations/0009_auto_20200210_1032.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.9 on 2020-02-10 10:32 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0008_auto_20200201_1247'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='creation_date', - field=models.DateField(auto_now_add=True), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0010_auto_20200318_1027.py b/src/backend/InvenTree/build/migrations/0010_auto_20200318_1027.py deleted file mode 100644 index d6871c8431..0000000000 --- a/src/backend/InvenTree/build/migrations/0010_auto_20200318_1027.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.9 on 2020-03-18 10:27 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0009_auto_20200210_1032'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='part', - field=models.ForeignKey(help_text='Select part to build', limit_choices_to={'active': True, 'assembly': True, 'is_template': False, 'virtual': False}, on_delete=django.db.models.deletion.CASCADE, related_name='builds', to='part.Part'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0011_auto_20200406_0123.py b/src/backend/InvenTree/build/migrations/0011_auto_20200406_0123.py deleted file mode 100644 index ba081c5e13..0000000000 --- a/src/backend/InvenTree/build/migrations/0011_auto_20200406_0123.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.10 on 2020-04-06 01:23 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0010_auto_20200318_1027'), - ] - - operations = [ - migrations.RenameField( - model_name='build', - old_name='URL', - new_name='link', - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0012_build_sales_order.py b/src/backend/InvenTree/build/migrations/0012_build_sales_order.py deleted file mode 100644 index cd2860919f..0000000000 --- a/src/backend/InvenTree/build/migrations/0012_build_sales_order.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.0.5 on 2020-04-24 22:51 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0029_auto_20200423_1042'), - ('build', '0011_auto_20200406_0123'), - ] - - operations = [ - migrations.AddField( - model_name='build', - name='sales_order', - field=models.ForeignKey(blank=True, help_text='Sales Order to which this build is allocated', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds', to='order.SalesOrder'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0013_auto_20200425_0507.py b/src/backend/InvenTree/build/migrations/0013_auto_20200425_0507.py deleted file mode 100644 index 15be35eea6..0000000000 --- a/src/backend/InvenTree/build/migrations/0013_auto_20200425_0507.py +++ /dev/null @@ -1,46 +0,0 @@ -# Generated by Django 3.0.5 on 2020-04-25 05:07 - -from django.db import migrations, models -import django.db.models.deletion -import mptt.fields - - -class Migration(migrations.Migration): - - atomic = False - - dependencies = [ - ('build', '0012_build_sales_order'), - ] - - operations = [ - migrations.AddField( - model_name='build', - name='level', - field=models.PositiveIntegerField(default=0, editable=False), - preserve_default=False, - ), - migrations.AddField( - model_name='build', - name='lft', - field=models.PositiveIntegerField(default=0, editable=False), - preserve_default=False, - ), - migrations.AddField( - model_name='build', - name='parent', - field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='build.Build'), - ), - migrations.AddField( - model_name='build', - name='rght', - field=models.PositiveIntegerField(default=0, editable=False), - preserve_default=False, - ), - migrations.AddField( - model_name='build', - name='tree_id', - field=models.PositiveIntegerField(db_index=True, default=0, editable=False), - preserve_default=False, - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0014_auto_20200425_1243.py b/src/backend/InvenTree/build/migrations/0014_auto_20200425_1243.py deleted file mode 100644 index 88fece94ba..0000000000 --- a/src/backend/InvenTree/build/migrations/0014_auto_20200425_1243.py +++ /dev/null @@ -1,70 +0,0 @@ -# Generated by Django 3.0.5 on 2020-04-25 12:43 - -import InvenTree.fields -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion -import mptt.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0035_auto_20200406_0045'), - ('stock', '0031_auto_20200422_0209'), - ('order', '0029_auto_20200423_1042'), - ('build', '0013_auto_20200425_0507'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='batch', - field=models.CharField(blank=True, help_text='Batch code for this build output', max_length=100, null=True, verbose_name='Batch Code'), - ), - migrations.AlterField( - model_name='build', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', verbose_name='External Link'), - ), - migrations.AlterField( - model_name='build', - name='notes', - field=models.TextField(blank=True, help_text='Extra build notes', verbose_name='Notes'), - ), - migrations.AlterField( - model_name='build', - name='parent', - field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='build.Build', verbose_name='Parent Build'), - ), - migrations.AlterField( - model_name='build', - name='part', - field=models.ForeignKey(help_text='Select part to build', limit_choices_to={'active': True, 'assembly': True, 'is_template': False, 'virtual': False}, on_delete=django.db.models.deletion.CASCADE, related_name='builds', to='part.Part', verbose_name='Part'), - ), - migrations.AlterField( - model_name='build', - name='quantity', - field=models.PositiveIntegerField(default=1, help_text='Number of parts to build', validators=[django.core.validators.MinValueValidator(1)], verbose_name='Build Quantity'), - ), - migrations.AlterField( - model_name='build', - name='sales_order', - field=models.ForeignKey(blank=True, help_text='Sales Order to which this build is allocated', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds', to='order.SalesOrder', verbose_name='Sales Order Reference'), - ), - migrations.AlterField( - model_name='build', - name='status', - field=models.PositiveIntegerField(choices=[(10, 'Pending'), (20, 'Allocated'), (30, 'Cancelled'), (40, 'Complete')], default=10, help_text='Build status code', validators=[django.core.validators.MinValueValidator(0)], verbose_name='Build Status'), - ), - migrations.AlterField( - model_name='build', - name='take_from', - field=models.ForeignKey(blank=True, help_text='Select location to take stock from for this build (leave blank to take from any stock location)', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sourcing_builds', to='stock.StockLocation', verbose_name='Source Location'), - ), - migrations.AlterField( - model_name='build', - name='title', - field=models.CharField(help_text='Brief description of the build', max_length=100, verbose_name='Build Title'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0015_auto_20200425_1350.py b/src/backend/InvenTree/build/migrations/0015_auto_20200425_1350.py deleted file mode 100644 index 4f57df066e..0000000000 --- a/src/backend/InvenTree/build/migrations/0015_auto_20200425_1350.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 3.0.5 on 2020-04-25 13:50 - -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion -import mptt.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0014_auto_20200425_1243'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='parent', - field=mptt.fields.TreeForeignKey(blank=True, help_text='Parent build to which this build is allocated', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='build.Build', verbose_name='Parent Build'), - ), - migrations.AlterField( - model_name='builditem', - name='quantity', - field=models.DecimalField(decimal_places=5, default=1, help_text='Stock quantity to allocate to build', max_digits=15, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0016_auto_20200426_0551.py b/src/backend/InvenTree/build/migrations/0016_auto_20200426_0551.py index f44a37712c..b0bf343c8b 100644 --- a/src/backend/InvenTree/build/migrations/0016_auto_20200426_0551.py +++ b/src/backend/InvenTree/build/migrations/0016_auto_20200426_0551.py @@ -8,7 +8,7 @@ class Migration(migrations.Migration): dependencies = [ ('stock', '0033_auto_20200426_0539'), - ('build', '0015_auto_20200425_1350'), + ('build', '0006_auto_20190913_1407_squashed_0015_auto_20200425_1350'), ] operations = [ diff --git a/src/backend/InvenTree/build/migrations/0017_auto_20200426_0612.py b/src/backend/InvenTree/build/migrations/0017_auto_20200426_0612.py deleted file mode 100644 index 83eb02ce35..0000000000 --- a/src/backend/InvenTree/build/migrations/0017_auto_20200426_0612.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.0.5 on 2020-04-26 06:12 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0034_auto_20200426_0602'), - ('build', '0016_auto_20200426_0551'), - ] - - operations = [ - migrations.AlterField( - model_name='builditem', - name='stock_item', - field=models.ForeignKey(help_text='Stock Item to allocate to build', limit_choices_to={'belongs_to': None, 'build_order': None, 'sales_order': None}, on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='stock.StockItem'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0017_auto_20200426_0612_squashed_0058_buildline_consumed.py b/src/backend/InvenTree/build/migrations/0017_auto_20200426_0612_squashed_0058_buildline_consumed.py new file mode 100644 index 0000000000..b3f291d160 --- /dev/null +++ b/src/backend/InvenTree/build/migrations/0017_auto_20200426_0612_squashed_0058_buildline_consumed.py @@ -0,0 +1,323 @@ +# Generated by Django 5.2.17 on 2026-09-09 00:01 + +import InvenTree.fields +import build.status_codes +import build.validators +import django.core.validators +import django.db.models.deletion +import generic.states.fields +import generic.states.validators +import mptt.fields +from django.conf import settings +from django.db import migrations, models + + +def update_build_reference(apps, schema_editor): + """Update the build order reference. + + Ref: https://github.com/inventree/InvenTree/pull/3267 + + Performs the following steps: + - Extract existing 'prefix' value + - Generate a build order pattern based on the prefix value + - Update any existing build order references with the specified prefix + """ + InvenTreeSetting = apps.get_model('common', 'inventreesetting') + + try: + prefix = InvenTreeSetting.objects.get(key='BUILDORDER_REFERENCE_PREFIX').value + except Exception: + prefix = 'BO-' + + pattern = prefix + '{ref:04d}' + + try: + setting = InvenTreeSetting.objects.get(key='BUILDORDER_REFERENCE_PATTERN') + setting.value = pattern + setting.save() + except InvenTreeSetting.DoesNotExist: + InvenTreeSetting.objects.create(key='BUILDORDER_REFERENCE_PATTERN', value=pattern) + + Build = apps.get_model('build', 'build') + + for build in Build.objects.all(): + if not build.reference.startswith(prefix): + build.reference = prefix + build.reference + build.save() + + +class Migration(migrations.Migration): + + atomic = False + + replaces = [('build', '0017_auto_20200426_0612'), ('build', '0018_build_reference'), ('build', '0019_auto_20201019_1302'), ('build', '0020_auto_20201019_1325'), ('build', '0021_auto_20201020_0908_squashed_0026_auto_20201023_1228'), ('build', '0022_buildorderattachment'), ('build', '0023_auto_20201110_0911'), ('build', '0024_auto_20201201_1023'), ('build', '0025_build_target_date'), ('build', '0026_auto_20210216_1539'), ('build', '0027_auto_20210404_2016'), ('build', '0028_builditem_bom_item'), ('build', '0029_auto_20210601_1525'), ('build', '0030_alter_build_reference'), ('build', '0031_build_reference_int'), ('build', '0032_auto_20211014_0632'), ('build', '0033_auto_20211128_0151'), ('build', '0034_alter_build_reference_int'), ('build', '0035_alter_build_notes'), ('build', '0036_auto_20220707_1101'), ('build', '0037_build_priority'), ('build', '0038_alter_build_responsible'), ('build', '0039_auto_20230317_0816'), ('build', '0040_auto_20230404_1310'), ('build', '0041_alter_build_title'), ('build', '0042_alter_build_notes'), ('build', '0043_buildline'), ('build', '0044_auto_20230528_1410'), ('build', '0045_builditem_build_line'), ('build', '0046_auto_20230606_1033'), ('build', '0047_auto_20230606_1058'), ('build', '0048_build_project_code'), ('build', '0049_alter_builditem_build_line'), ('build', '0050_auto_20240508_0138'), ('build', '0051_delete_buildorderattachment'), ('build', '0052_build_status_custom_key_alter_build_status'), ('build', '0053_alter_build_part'), ('build', '0054_build_start_date'), ('build', '0055_auto_20250221_1230'), ('build', '0056_alter_build_link'), ('build', '0057_build_external'), ('build', '0058_buildline_consumed')] + + dependencies = [ + ('build', '0016_auto_20200426_0551'), + ('common', '0019_projectcode_metadata'), + ('part', '0051_bomitem_optional'), + ('part', '0066_bomitem_allow_variants'), + ('part', '0109_auto_20230517_1048'), + ('stock', '0034_auto_20200426_0602'), + ('stock', '0052_stockitem_is_building'), + ('stock', '0054_remove_stockitem_build_order'), + ('stock', '0058_stockitem_packaging'), + ('users', '0005_owner_model'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name='builditem', + name='stock_item', + field=models.ForeignKey(help_text='Stock Item to allocate to build', limit_choices_to={'belongs_to': None, 'build_order': None, 'sales_order': None}, on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='stock.stockitem'), + ), + migrations.AddField( + model_name='build', + name='reference', + field=models.CharField(blank=True, help_text='Build Order Reference', max_length=64, verbose_name='Reference'), + ), + migrations.AlterField( + model_name='build', + name='reference', + field=models.CharField(help_text='Build Order Reference', max_length=64, unique=True, verbose_name='Reference'), + ), + migrations.AlterModelOptions( + name='build', + options={'verbose_name': 'Build Order', 'verbose_name_plural': 'Build Orders'}, + ), + migrations.AlterField( + model_name='build', + name='reference', + field=models.CharField(default=build.validators.generate_next_build_reference, help_text='Build Order Reference', max_length=64, unique=True, validators=[build.validators.validate_build_order_reference], verbose_name='Reference'), + ), + migrations.AlterField( + model_name='build', + name='title', + field=models.CharField(help_text='Brief description of the build', max_length=100, verbose_name='Description'), + ), + migrations.AddField( + model_name='build', + name='destination', + field=models.ForeignKey(blank=True, help_text='Select location where the completed items will be stored', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='incoming_builds', to='stock.stocklocation', verbose_name='Destination Location'), + ), + migrations.AlterField( + model_name='build', + name='status', + field=models.PositiveIntegerField(choices=[(10, 'Pending'), (20, 'Production'), (25, 'On Hold'), (30, 'Cancelled'), (40, 'Complete')], default=10, help_text='Build status code', validators=[django.core.validators.MinValueValidator(0)], verbose_name='Build Status'), + ), + migrations.AlterField( + model_name='build', + name='part', + field=models.ForeignKey(help_text='Select part to build', limit_choices_to={'active': True, 'assembly': True, 'virtual': False}, on_delete=django.db.models.deletion.CASCADE, related_name='builds', to='part.part', verbose_name='Part'), + ), + migrations.AddField( + model_name='build', + name='completed', + field=models.PositiveIntegerField(default=0, help_text='Number of stock items which have been completed', verbose_name='Completed items'), + ), + migrations.AlterField( + model_name='build', + name='quantity', + field=models.PositiveIntegerField(default=1, help_text='Number of stock items to build', validators=[django.core.validators.MinValueValidator(1)], verbose_name='Build Quantity'), + ), + migrations.AlterField( + model_name='build', + name='parent', + field=mptt.fields.TreeForeignKey(blank=True, help_text='Build Order to which this build is allocated', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children', to='build.build', verbose_name='Parent Build'), + ), + migrations.AddField( + model_name='build', + name='target_date', + field=models.DateField(blank=True, help_text='Target date for build completion. Build will be overdue after this date.', null=True, verbose_name='Target completion date'), + ), + migrations.AlterField( + model_name='build', + name='completed_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds_completed', to=settings.AUTH_USER_MODEL, verbose_name='completed by'), + ), + migrations.AlterField( + model_name='build', + name='completion_date', + field=models.DateField(blank=True, null=True, verbose_name='Completion Date'), + ), + migrations.AlterField( + model_name='build', + name='creation_date', + field=models.DateField(auto_now_add=True, verbose_name='Creation Date'), + ), + migrations.AddField( + model_name='build', + name='issued_by', + field=models.ForeignKey(blank=True, help_text='User who issued this build order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds_issued', to=settings.AUTH_USER_MODEL, verbose_name='Issued by'), + ), + migrations.AddField( + model_name='build', + name='responsible', + field=models.ForeignKey(blank=True, help_text='User responsible for this build order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds_responsible', to='users.owner', verbose_name='Responsible'), + ), + migrations.AddField( + model_name='builditem', + name='install_into', + field=models.ForeignKey(blank=True, help_text='Destination stock item', limit_choices_to={'is_building': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='items_to_install', to='stock.stockitem', verbose_name='Install into'), + ), + migrations.AlterField( + model_name='builditem', + name='quantity', + field=models.DecimalField(decimal_places=5, default=1, help_text='Stock quantity to allocate to build', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'), + ), + migrations.AlterField( + model_name='builditem', + name='stock_item', + field=models.ForeignKey(help_text='Source stock item', limit_choices_to={'belongs_to': None, 'sales_order': None}, on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='stock.stockitem', verbose_name='Stock Item'), + ), + migrations.CreateModel( + name='BuildOrderAttachment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('attachment', models.FileField(help_text='Select file to attach', upload_to='attachments', verbose_name='Attachment')), + ('comment', models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment')), + ('upload_date', models.DateField(auto_now_add=True, null=True, verbose_name='upload date')), + ('build', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='build.build')), + ('user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='build', + name='reference_int', + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name='buildorderattachment', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=2000, null=True, verbose_name='Link'), + ), + migrations.AlterField( + model_name='buildorderattachment', + name='attachment', + field=models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to='attachments', verbose_name='Attachment'), + ), + migrations.AlterField( + model_name='build', + name='reference_int', + field=models.BigIntegerField(default=0), + ), + migrations.AlterField( + model_name='build', + name='notes', + field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Extra build notes', max_length=50000, null=True, verbose_name='Notes'), + ), + migrations.RunPython(code=update_build_reference, reverse_code=migrations.RunPython.noop), + migrations.AddField( + model_name='build', + name='priority', + field=models.PositiveIntegerField(default=0, help_text='Priority of this build order', validators=[django.core.validators.MinValueValidator(0)], verbose_name='Build Priority'), + ), + migrations.AlterField( + model_name='build', + name='responsible', + field=models.ForeignKey(blank=True, help_text='User or group responsible for this build order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds_responsible', to='users.owner', verbose_name='Responsible'), + ), + migrations.AddField( + model_name='build', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AddField( + model_name='builditem', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AddField( + model_name='build', + name='barcode_data', + field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), + ), + migrations.AddField( + model_name='build', + name='barcode_hash', + field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), + ), + migrations.AlterField( + model_name='build', + name='title', + field=models.CharField(blank=True, help_text='Brief description of the build (optional)', max_length=100, verbose_name='Description'), + ), + migrations.AlterField( + model_name='build', + name='notes', + field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), + ), + migrations.CreateModel( + name='BuildLine', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity', models.DecimalField(decimal_places=5, default=1, help_text='Required quantity for build order', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity')), + ('bom_item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='build_lines', to='part.bomitem')), + ('build', models.ForeignKey(help_text='Build object', on_delete=django.db.models.deletion.CASCADE, related_name='build_lines', to='build.build')), + ], + options={ + 'unique_together': {('build', 'bom_item')}, + 'verbose_name': 'Build Order Line Item', + }, + ), + migrations.AddField( + model_name='builditem', + name='build_line', + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='build.buildline'), + ), + migrations.AlterUniqueTogether( + name='builditem', + unique_together={('build_line', 'stock_item', 'install_into')}, + ), + migrations.RemoveField( + model_name='builditem', + name='build', + ), + migrations.AddField( + model_name='build', + name='project_code', + field=models.ForeignKey(blank=True, help_text='Project code for this build order', null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.projectcode', verbose_name='Project Code'), + ), + migrations.DeleteModel( + name='BuildOrderAttachment', + ), + migrations.AddField( + model_name='build', + name='status_custom_key', + field=generic.states.fields.ExtraInvenTreeCustomStatusModelField(blank=True, default=None, help_text='Additional status information for this item', null=True, validators=[generic.states.validators.CustomStatusCodeValidator(status_class=build.status_codes.BuildStatus)], verbose_name='Custom status key'), + ), + migrations.AlterField( + model_name='build', + name='status', + field=generic.states.fields.InvenTreeCustomStatusModelField(choices=[(10, 'Pending'), (20, 'Production'), (25, 'On Hold'), (30, 'Cancelled'), (40, 'Complete')], default=10, help_text='Build status code', validators=[django.core.validators.MinValueValidator(0), generic.states.validators.CustomStatusCodeValidator(status_class=build.status_codes.BuildStatus)], verbose_name='Build Status'), + ), + migrations.AlterField( + model_name='build', + name='part', + field=models.ForeignKey(help_text='Select part to build', limit_choices_to={'assembly': True}, on_delete=django.db.models.deletion.CASCADE, related_name='builds', to='part.part', verbose_name='Part'), + ), + migrations.AddField( + model_name='build', + name='start_date', + field=models.DateField(blank=True, help_text='Scheduled start date for this build order', null=True, verbose_name='Build start date'), + ), + migrations.AlterField( + model_name='build', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=2000, verbose_name='External Link'), + ), + migrations.AddField( + model_name='build', + name='external', + field=models.BooleanField(default=False, help_text='This build order is fulfilled externally', verbose_name='External Build'), + ), + migrations.AddField( + model_name='buildline', + name='consumed', + field=models.DecimalField(decimal_places=5, default=0, help_text='Quantity of consumed stock', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Consumed'), + ), + ] diff --git a/src/backend/InvenTree/build/migrations/0018_build_reference.py b/src/backend/InvenTree/build/migrations/0018_build_reference.py deleted file mode 100644 index 8fd790b753..0000000000 --- a/src/backend/InvenTree/build/migrations/0018_build_reference.py +++ /dev/null @@ -1,60 +0,0 @@ -# Generated by Django 3.0.7 on 2020-10-19 11:25 - -from django.db import migrations, models - - -def add_default_reference(apps, schema_editor): - """ - Add a "default" build-order reference for any existing build orders. - Best we can do is use the PK of the build order itself. - """ - - Build = apps.get_model('build', 'build') - - count = 0 - - for build in Build.objects.all(): - - build.reference = str(build.pk) - build.save() - count += 1 - - if count > 0: - print(f"\nUpdated build reference for {count} existing BuildOrder objects") - - -class Migration(migrations.Migration): - - atomic = False - - dependencies = [ - ('build', '0017_auto_20200426_0612'), - ] - - operations = [ - # Initial operation - create a 'reference' field for the Build object: - migrations.AddField( - model_name='build', - name='reference', - field=models.CharField(help_text='Build Order Reference', blank=True, max_length=64, unique=False, verbose_name='Reference'), - ), - - # Auto-populate the new reference field for any existing build order objects - migrations.RunPython( - add_default_reference, - reverse_code=migrations.RunPython.noop - ), - - # Now that each build has a non-empty, unique reference, update the field requirements! - migrations.AlterField( - model_name='build', - name='reference', - field=models.CharField( - help_text='Build Order Reference', - max_length=64, - blank=False, - unique=True, - verbose_name='Reference' - ) - ) - ] diff --git a/src/backend/InvenTree/build/migrations/0019_auto_20201019_1302.py b/src/backend/InvenTree/build/migrations/0019_auto_20201019_1302.py deleted file mode 100644 index 9b12c5d021..0000000000 --- a/src/backend/InvenTree/build/migrations/0019_auto_20201019_1302.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.0.7 on 2020-10-19 13:02 - -import build.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0018_build_reference'), - ] - - operations = [ - migrations.AlterModelOptions( - name='build', - options={'verbose_name': 'Build Order', 'verbose_name_plural': 'Build Orders'}, - ), - migrations.AlterField( - model_name='build', - name='reference', - field=models.CharField(help_text='Build Order Reference', max_length=64, unique=True, validators=[build.validators.validate_build_order_reference], verbose_name='Reference'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0020_auto_20201019_1325.py b/src/backend/InvenTree/build/migrations/0020_auto_20201019_1325.py deleted file mode 100644 index 1406530c8d..0000000000 --- a/src/backend/InvenTree/build/migrations/0020_auto_20201019_1325.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.7 on 2020-10-19 13:25 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0019_auto_20201019_1302'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='title', - field=models.CharField(help_text='Brief description of the build', max_length=100, verbose_name='Description'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0021_auto_20201020_0908_squashed_0026_auto_20201023_1228.py b/src/backend/InvenTree/build/migrations/0021_auto_20201020_0908_squashed_0026_auto_20201023_1228.py deleted file mode 100644 index db2803f53f..0000000000 --- a/src/backend/InvenTree/build/migrations/0021_auto_20201020_0908_squashed_0026_auto_20201023_1228.py +++ /dev/null @@ -1,66 +0,0 @@ -# Generated by Django 3.0.7 on 2020-10-25 21:33 - -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion -import mptt.fields - -from build.status_codes import BuildStatus - - -class Migration(migrations.Migration): - - replaces = [('build', '0021_auto_20201020_0908'), ('build', '0022_auto_20201020_0953'), ('build', '0023_auto_20201020_1009'), ('build', '0024_auto_20201020_1144'), ('build', '0025_auto_20201020_1248'), ('build', '0026_auto_20201023_1228')] - - dependencies = [ - ('stock', '0052_stockitem_is_building'), - ('build', '0020_auto_20201019_1325'), - ('part', '0051_bomitem_optional'), - ] - - operations = [ - migrations.AddField( - model_name='builditem', - name='install_into', - field=models.ForeignKey(blank=True, help_text='Destination stock item', limit_choices_to={'is_building': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='items_to_install', to='stock.StockItem'), - ), - migrations.AlterField( - model_name='builditem', - name='stock_item', - field=models.ForeignKey(help_text='Source stock item', limit_choices_to={'belongs_to': None, 'build_order': None, 'sales_order': None}, on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='stock.StockItem'), - ), - migrations.AddField( - model_name='build', - name='destination', - field=models.ForeignKey(blank=True, help_text='Select location where the completed items will be stored', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='incoming_builds', to='stock.StockLocation', verbose_name='Destination Location'), - ), - migrations.AlterField( - model_name='build', - name='parent', - field=mptt.fields.TreeForeignKey(blank=True, help_text='Build Order to which this build is allocated', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='build.Build', verbose_name='Parent Build'), - ), - migrations.AlterField( - model_name='build', - name='status', - field=models.PositiveIntegerField(choices=BuildStatus.items(), default=BuildStatus.PENDING.value, help_text='Build status code', validators=[django.core.validators.MinValueValidator(0)], verbose_name='Build Status'), - ), - migrations.AlterField( - model_name='build', - name='part', - field=models.ForeignKey(help_text='Select part to build', limit_choices_to={'active': True, 'assembly': True, 'virtual': False}, on_delete=django.db.models.deletion.CASCADE, related_name='builds', to='part.Part', verbose_name='Part'), - ), - migrations.AddField( - model_name='build', - name='completed', - field=models.PositiveIntegerField(default=0, help_text='Number of stock items which have been completed', verbose_name='Completed items'), - ), - migrations.AlterField( - model_name='build', - name='quantity', - field=models.PositiveIntegerField(default=1, help_text='Number of stock items to build', validators=[django.core.validators.MinValueValidator(1)], verbose_name='Build Quantity'), - ), - migrations.AlterUniqueTogether( - name='builditem', - unique_together={('build', 'stock_item', 'install_into')}, - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0022_buildorderattachment.py b/src/backend/InvenTree/build/migrations/0022_buildorderattachment.py deleted file mode 100644 index 47ecbf7f98..0000000000 --- a/src/backend/InvenTree/build/migrations/0022_buildorderattachment.py +++ /dev/null @@ -1,31 +0,0 @@ -# Generated by Django 3.0.7 on 2020-10-26 04:17 - -import InvenTree.models -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('build', '0021_auto_20201020_0908_squashed_0026_auto_20201023_1228'), - ] - - operations = [ - migrations.CreateModel( - name='BuildOrderAttachment', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('attachment', models.FileField(help_text='Select file to attach', upload_to='attachments')), - ('comment', models.CharField(blank=True, help_text='File comment', max_length=100)), - ('upload_date', models.DateField(auto_now_add=True, null=True)), - ('build', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='build.Build')), - ('user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), - ], - options={ - 'abstract': False, - }, - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0023_auto_20201110_0911.py b/src/backend/InvenTree/build/migrations/0023_auto_20201110_0911.py deleted file mode 100644 index 3d95e6844e..0000000000 --- a/src/backend/InvenTree/build/migrations/0023_auto_20201110_0911.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-10 09:11 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0054_remove_stockitem_build_order'), - ('build', '0022_buildorderattachment'), - ] - - operations = [ - migrations.AlterField( - model_name='builditem', - name='stock_item', - field=models.ForeignKey(help_text='Source stock item', limit_choices_to={'belongs_to': None, 'sales_order': None}, on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='stock.StockItem'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0024_auto_20201201_1023.py b/src/backend/InvenTree/build/migrations/0024_auto_20201201_1023.py deleted file mode 100644 index 4e995fdf80..0000000000 --- a/src/backend/InvenTree/build/migrations/0024_auto_20201201_1023.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-30 23:23 - -from django.db import migrations -import django.db.models.deletion -import mptt.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0023_auto_20201110_0911'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='parent', - field=mptt.fields.TreeForeignKey(blank=True, help_text='Build Order to which this build is allocated', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children', to='build.Build', verbose_name='Parent Build'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0025_build_target_date.py b/src/backend/InvenTree/build/migrations/0025_build_target_date.py deleted file mode 100644 index e834d74d0f..0000000000 --- a/src/backend/InvenTree/build/migrations/0025_build_target_date.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.7 on 2020-12-15 12:13 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0024_auto_20201201_1023'), - ] - - operations = [ - migrations.AddField( - model_name='build', - name='target_date', - field=models.DateField(blank=True, help_text='Target date for build completion. Build will be overdue after this date.', null=True, verbose_name='Target completion date'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0026_auto_20210216_1539.py b/src/backend/InvenTree/build/migrations/0026_auto_20210216_1539.py deleted file mode 100644 index aee7c44bd6..0000000000 --- a/src/backend/InvenTree/build/migrations/0026_auto_20210216_1539.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 3.0.7 on 2021-02-16 04:39 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0005_owner_model'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('build', '0025_build_target_date'), - ] - - operations = [ - migrations.AddField( - model_name='build', - name='issued_by', - field=models.ForeignKey(blank=True, help_text='User who issued this build order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds_issued', to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='build', - name='responsible', - field=models.ForeignKey(blank=True, help_text='User responsible for this build order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds_responsible', to='users.Owner'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0027_auto_20210404_2016.py b/src/backend/InvenTree/build/migrations/0027_auto_20210404_2016.py deleted file mode 100644 index 6d34eae5b5..0000000000 --- a/src/backend/InvenTree/build/migrations/0027_auto_20210404_2016.py +++ /dev/null @@ -1,85 +0,0 @@ -# Generated by Django 3.0.7 on 2021-04-04 20:16 - -import InvenTree.models -from django.conf import settings -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0058_stockitem_packaging'), - ('users', '0005_owner_model'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('build', '0026_auto_20210216_1539'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='completed_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds_completed', to=settings.AUTH_USER_MODEL, verbose_name='completed by'), - ), - migrations.AlterField( - model_name='build', - name='completion_date', - field=models.DateField(blank=True, null=True, verbose_name='Completion Date'), - ), - migrations.AlterField( - model_name='build', - name='creation_date', - field=models.DateField(auto_now_add=True, verbose_name='Creation Date'), - ), - migrations.AlterField( - model_name='build', - name='issued_by', - field=models.ForeignKey(blank=True, help_text='User who issued this build order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds_issued', to=settings.AUTH_USER_MODEL, verbose_name='Issued by'), - ), - migrations.AlterField( - model_name='build', - name='responsible', - field=models.ForeignKey(blank=True, help_text='User responsible for this build order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds_responsible', to='users.Owner', verbose_name='Responsible'), - ), - migrations.AlterField( - model_name='builditem', - name='build', - field=models.ForeignKey(help_text='Build to allocate parts', on_delete=django.db.models.deletion.CASCADE, related_name='allocated_stock', to='build.Build', verbose_name='Build'), - ), - migrations.AlterField( - model_name='builditem', - name='install_into', - field=models.ForeignKey(blank=True, help_text='Destination stock item', limit_choices_to={'is_building': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='items_to_install', to='stock.StockItem', verbose_name='Install into'), - ), - migrations.AlterField( - model_name='builditem', - name='quantity', - field=models.DecimalField(decimal_places=5, default=1, help_text='Stock quantity to allocate to build', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'), - ), - migrations.AlterField( - model_name='builditem', - name='stock_item', - field=models.ForeignKey(help_text='Source stock item', limit_choices_to={'belongs_to': None, 'sales_order': None}, on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='stock.StockItem', verbose_name='Stock Item'), - ), - migrations.AlterField( - model_name='buildorderattachment', - name='attachment', - field=models.FileField(help_text='Select file to attach', upload_to='attachments', verbose_name='Attachment'), - ), - migrations.AlterField( - model_name='buildorderattachment', - name='comment', - field=models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment'), - ), - migrations.AlterField( - model_name='buildorderattachment', - name='upload_date', - field=models.DateField(auto_now_add=True, null=True, verbose_name='upload date'), - ), - migrations.AlterField( - model_name='buildorderattachment', - name='user', - field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0028_builditem_bom_item.py b/src/backend/InvenTree/build/migrations/0028_builditem_bom_item.py deleted file mode 100644 index f93c63dc4c..0000000000 --- a/src/backend/InvenTree/build/migrations/0028_builditem_bom_item.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2 on 2021-06-01 05:23 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0066_bomitem_allow_variants'), - ('build', '0027_auto_20210404_2016'), - ] - - operations = [ - migrations.AddField( - model_name='builditem', - name='bom_item', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='allocate_build_items', to='part.bomitem'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0029_auto_20210601_1525.py b/src/backend/InvenTree/build/migrations/0029_auto_20210601_1525.py deleted file mode 100644 index 3c98b504df..0000000000 --- a/src/backend/InvenTree/build/migrations/0029_auto_20210601_1525.py +++ /dev/null @@ -1,60 +0,0 @@ -# Generated by Django 3.2 on 2021-06-01 05:25 - -import logging - -from django.db import migrations - - -logger = logging.getLogger('inventree') - - -def assign_bom_items(apps, schema_editor): - """ - Run through existing BuildItem objects, - and assign a matching BomItem - """ - - BuildItem = apps.get_model('build', 'builditem') - BomItem = apps.get_model('part', 'bomitem') - Part = apps.get_model('part', 'part') - - count_valid = 0 - count_total = 0 - - for build_item in BuildItem.objects.all(): # pragma: no cover - - # Try to find a BomItem which matches the BuildItem - # Note: Before this migration, variant stock assignment was not allowed, - # so BomItem lookup should be pretty easy - - if count_total == 0: - # First time around - logger.info("Assigning BomItems to existing BuildItem objects") - - count_total += 1 - - try: - bom_item = BomItem.objects.get( - part__id=build_item.build.part.pk, - sub_part__id=build_item.stock_item.part.pk, - ) - - build_item.bom_item = bom_item - build_item.save() - - count_valid += 1 - - except BomItem.DoesNotExist: - pass - - if count_total > 0: # pragma: no cover - logger.info(f"Assigned BomItem for {count_valid}/{count_total} entries") - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0028_builditem_bom_item'), - ] - - operations = [] diff --git a/src/backend/InvenTree/build/migrations/0030_alter_build_reference.py b/src/backend/InvenTree/build/migrations/0030_alter_build_reference.py deleted file mode 100644 index 9f940e7eae..0000000000 --- a/src/backend/InvenTree/build/migrations/0030_alter_build_reference.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.4 on 2021-07-08 14:14 - -import build.validators -import build.models -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0029_auto_20210601_1525'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='reference', - field=models.CharField(default=build.validators.generate_next_build_reference, help_text='Build Order Reference', max_length=64, unique=True, validators=[build.validators.validate_build_order_reference], verbose_name='Reference'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0031_build_reference_int.py b/src/backend/InvenTree/build/migrations/0031_build_reference_int.py deleted file mode 100644 index c7fc2c16cc..0000000000 --- a/src/backend/InvenTree/build/migrations/0031_build_reference_int.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.5 on 2021-10-14 06:23 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0030_alter_build_reference'), - ] - - operations = [ - migrations.AddField( - model_name='build', - name='reference_int', - field=models.IntegerField(default=0), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0032_auto_20211014_0632.py b/src/backend/InvenTree/build/migrations/0032_auto_20211014_0632.py deleted file mode 100644 index 52ce1d5f7d..0000000000 --- a/src/backend/InvenTree/build/migrations/0032_auto_20211014_0632.py +++ /dev/null @@ -1,48 +0,0 @@ -# Generated by Django 3.2.5 on 2021-10-14 06:32 - -import re - -from django.db import migrations - - -def build_refs(apps, schema_editor): - """ - Rebuild the integer "reference fields" for existing Build objects - """ - - BuildOrder = apps.get_model('build', 'build') - - for build in BuildOrder.objects.all(): - - ref = 0 - - result = re.match(r"^(\d+)", build.reference) - - if result and len(result.groups()) == 1: - try: - ref = int(result.groups()[0]) - except Exception: # pragma: no cover - ref = 0 - - # Clip integer value to ensure it does not overflow database field - if ref > 0x7fffffff: - ref = 0x7fffffff - - build.reference_int = ref - build.save() - - -class Migration(migrations.Migration): - - atomic = False - - dependencies = [ - ('build', '0031_build_reference_int'), - ] - - operations = [ - migrations.RunPython( - build_refs, - reverse_code=migrations.RunPython.noop - ) - ] diff --git a/src/backend/InvenTree/build/migrations/0033_auto_20211128_0151.py b/src/backend/InvenTree/build/migrations/0033_auto_20211128_0151.py deleted file mode 100644 index 5558fe8973..0000000000 --- a/src/backend/InvenTree/build/migrations/0033_auto_20211128_0151.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 3.2.5 on 2021-11-28 01:51 - -import InvenTree.fields -import InvenTree.models -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0032_auto_20211014_0632'), - ] - - operations = [ - migrations.AddField( - model_name='buildorderattachment', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', null=True, verbose_name='Link'), - ), - migrations.AlterField( - model_name='buildorderattachment', - name='attachment', - field=models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to='attachments', verbose_name='Attachment'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0034_alter_build_reference_int.py b/src/backend/InvenTree/build/migrations/0034_alter_build_reference_int.py deleted file mode 100644 index 8b14d3812e..0000000000 --- a/src/backend/InvenTree/build/migrations/0034_alter_build_reference_int.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.5 on 2021-12-01 21:39 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0033_auto_20211128_0151'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='reference_int', - field=models.BigIntegerField(default=0), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0035_alter_build_notes.py b/src/backend/InvenTree/build/migrations/0035_alter_build_notes.py deleted file mode 100644 index a86570069a..0000000000 --- a/src/backend/InvenTree/build/migrations/0035_alter_build_notes.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-20 07:28 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0034_alter_build_reference_int'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='notes', - field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Extra build notes', max_length=50000, null=True, verbose_name='Notes'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0036_auto_20220707_1101.py b/src/backend/InvenTree/build/migrations/0036_auto_20220707_1101.py deleted file mode 100644 index bf52780791..0000000000 --- a/src/backend/InvenTree/build/migrations/0036_auto_20220707_1101.py +++ /dev/null @@ -1,64 +0,0 @@ -# Generated by Django 3.2.14 on 2022-07-07 11:01 - -from django.db import migrations - - -def update_build_reference(apps, schema_editor): - """Update the build order reference. - - Ref: https://github.com/inventree/InvenTree/pull/3267 - - Performs the following steps: - - Extract existing 'prefix' value - - Generate a build order pattern based on the prefix value - - Update any existing build order references with the specified prefix - """ - - InvenTreeSetting = apps.get_model('common', 'inventreesetting') - - try: - prefix = InvenTreeSetting.objects.get(key='BUILDORDER_REFERENCE_PREFIX').value - except Exception: - prefix = 'BO-' - - # Construct a reference pattern - pattern = prefix + '{ref:04d}' - - # Create or update the BuildOrder.reference pattern - try: - setting = InvenTreeSetting.objects.get(key='BUILDORDER_REFERENCE_PATTERN') - setting.value = pattern - setting.save() - except InvenTreeSetting.DoesNotExist: - setting = InvenTreeSetting.objects.create( - key='BUILDORDER_REFERENCE_PATTERN', - value=pattern, - ) - - # Update any existing build order references with the prefix - Build = apps.get_model('build', 'build') - - n = 0 - - for build in Build.objects.all(): - if not build.reference.startswith(prefix): - build.reference = prefix + build.reference - build.save() - n += 1 - - if n > 0: - print(f"Updated reference field for {n} BuildOrder objects") - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0035_alter_build_notes'), - ] - - operations = [ - migrations.RunPython( - update_build_reference, - reverse_code=migrations.RunPython.noop, - ) - ] diff --git a/src/backend/InvenTree/build/migrations/0037_build_priority.py b/src/backend/InvenTree/build/migrations/0037_build_priority.py deleted file mode 100644 index 782e29501b..0000000000 --- a/src/backend/InvenTree/build/migrations/0037_build_priority.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.16 on 2023-01-17 20:37 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0036_auto_20220707_1101'), - ] - - operations = [ - migrations.AddField( - model_name='build', - name='priority', - field=models.PositiveIntegerField(default=0, help_text='Priority of this build order', validators=[django.core.validators.MinValueValidator(0)], verbose_name='Build Priority'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0038_alter_build_responsible.py b/src/backend/InvenTree/build/migrations/0038_alter_build_responsible.py deleted file mode 100644 index 09953c01ab..0000000000 --- a/src/backend/InvenTree/build/migrations/0038_alter_build_responsible.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.16 on 2023-02-09 23:52 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0005_owner_model'), - ('build', '0037_build_priority'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='responsible', - field=models.ForeignKey(blank=True, help_text='User or group responsible for this build order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='builds_responsible', to='users.owner', verbose_name='Responsible'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0039_auto_20230317_0816.py b/src/backend/InvenTree/build/migrations/0039_auto_20230317_0816.py deleted file mode 100644 index d62273d278..0000000000 --- a/src/backend/InvenTree/build/migrations/0039_auto_20230317_0816.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-17 08:16 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0038_alter_build_responsible'), - ] - - operations = [ - migrations.AddField( - model_name='build', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - migrations.AddField( - model_name='builditem', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0040_auto_20230404_1310.py b/src/backend/InvenTree/build/migrations/0040_auto_20230404_1310.py deleted file mode 100644 index fcb29e7e3a..0000000000 --- a/src/backend/InvenTree/build/migrations/0040_auto_20230404_1310.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-04 13:10 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0039_auto_20230317_0816'), - ] - - operations = [ - migrations.AddField( - model_name='build', - name='barcode_data', - field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), - ), - migrations.AddField( - model_name='build', - name='barcode_hash', - field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0041_alter_build_title.py b/src/backend/InvenTree/build/migrations/0041_alter_build_title.py deleted file mode 100644 index 3962587526..0000000000 --- a/src/backend/InvenTree/build/migrations/0041_alter_build_title.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-12 17:52 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0040_auto_20230404_1310'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='title', - field=models.CharField(blank=True, help_text='Brief description of the build (optional)', max_length=100, verbose_name='Description'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0042_alter_build_notes.py b/src/backend/InvenTree/build/migrations/0042_alter_build_notes.py deleted file mode 100644 index ed2bc9b411..0000000000 --- a/src/backend/InvenTree/build/migrations/0042_alter_build_notes.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-19 00:37 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0041_alter_build_title'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='notes', - field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0043_buildline.py b/src/backend/InvenTree/build/migrations/0043_buildline.py deleted file mode 100644 index 981b533649..0000000000 --- a/src/backend/InvenTree/build/migrations/0043_buildline.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-19 06:04 - -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0109_auto_20230517_1048'), - ('build', '0042_alter_build_notes'), - ] - - operations = [ - migrations.CreateModel( - name='BuildLine', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('quantity', models.DecimalField(decimal_places=5, default=1, help_text='Required quantity for build order', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity')), - ('bom_item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='build_lines', to='part.bomitem')), - ('build', models.ForeignKey(help_text='Build object', on_delete=django.db.models.deletion.CASCADE, related_name='build_lines', to='build.build')), - ], - options={ - 'unique_together': {('build', 'bom_item')}, - 'verbose_name': 'Build Order Line Item', - }, - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0044_auto_20230528_1410.py b/src/backend/InvenTree/build/migrations/0044_auto_20230528_1410.py deleted file mode 100644 index 6b7da23155..0000000000 --- a/src/backend/InvenTree/build/migrations/0044_auto_20230528_1410.py +++ /dev/null @@ -1,97 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-28 14:10 - -from django.db import migrations - - -def get_bom_items_for_part(part, Part, BomItem): - """ Return a list of all BOM items for a given part. - - Note that we cannot use the ORM here (as we are inside a data migration), - so we *copy* the logic from the Part class. - - This is a snapshot of the Part.get_bom_items() method as of 2023-05-29 - """ - - bom_items = set() - - # Get all BOM items which directly reference the part - for bom_item in BomItem.objects.filter(part=part): - bom_items.add(bom_item) - - # Get all BOM items which are inherited by the part - parents = Part.objects.filter( - tree_id=part.tree_id, - level__lt=part.level, - lft__lt=part.lft, - rght__gt=part.rght - ) - - for bom_item in BomItem.objects.filter(part__in=parents, inherited=True): - bom_items.add(bom_item) - - return list(bom_items) - - -def add_lines_to_builds(apps, schema_editor): - """Create BuildOrderLine objects for existing build orders""" - - # Get database models - Build = apps.get_model("build", "Build") - BuildLine = apps.get_model("build", "BuildLine") - - Part = apps.get_model("part", "Part") - BomItem = apps.get_model("part", "BomItem") - - build_lines = [] - - builds = Build.objects.all() - - if builds.count() > 0: - print(f"Creating BuildOrderLine objects for {builds.count()} existing builds") - - for build in builds: - # Create a BuildOrderLine for each BuildItem - - bom_items = get_bom_items_for_part(build.part, Part, BomItem) - - for item in bom_items: - build_lines.append( - BuildLine( - build=build, - bom_item=item, - quantity=item.quantity * build.quantity, - ) - ) - - if len(build_lines) > 0: - # Construct the new BuildLine objects - BuildLine.objects.bulk_create(build_lines) - print(f"Created {len(build_lines)} BuildOrderLine objects for existing builds") - - -def remove_build_lines(apps, schema_editor): - """Remove BuildOrderLine objects from the database""" - - # Get database models - BuildLine = apps.get_model("build", "BuildLine") - - n = BuildLine.objects.all().count() - - BuildLine.objects.all().delete() - - if n > 0: - print(f"Removed {n} BuildOrderLine objects") - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0043_buildline'), - ] - - operations = [ - migrations.RunPython( - add_lines_to_builds, - reverse_code=remove_build_lines, - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0045_builditem_build_line.py b/src/backend/InvenTree/build/migrations/0045_builditem_build_line.py deleted file mode 100644 index 690bb0352d..0000000000 --- a/src/backend/InvenTree/build/migrations/0045_builditem_build_line.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.19 on 2023-06-06 10:30 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0044_auto_20230528_1410'), - ] - - operations = [ - migrations.AddField( - model_name='builditem', - name='build_line', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='allocations', to='build.buildline'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0046_auto_20230606_1033.py b/src/backend/InvenTree/build/migrations/0046_auto_20230606_1033.py deleted file mode 100644 index 60d290a70f..0000000000 --- a/src/backend/InvenTree/build/migrations/0046_auto_20230606_1033.py +++ /dev/null @@ -1,95 +0,0 @@ -# Generated by Django 3.2.19 on 2023-06-06 10:33 - -import logging - -from django.db import migrations - - -logger = logging.getLogger('inventree') - - -def add_build_line_links(apps, schema_editor): - """Data migration to add links between BuildLine and BuildItem objects. - - Associated model types: - Build: A "Build Order" - BomItem: An individual line in the BOM for Build.part - BuildItem: An individual stock allocation against the Build Order - BuildLine: (new model) an individual line in the Build Order - - Goals: - - Find all BuildItem objects which are associated with a Build - - Link them against the relevant BuildLine object - - The BuildLine objects should have been created in 0044_auto_20230528_1410.py - """ - - BuildItem = apps.get_model("build", "BuildItem") - BuildLine = apps.get_model("build", "BuildLine") - - # Find any existing BuildItem objects - build_items = BuildItem.objects.all() - - n_missing = 0 - - for item in build_items: - - # Find the relevant BuildLine object - line = BuildLine.objects.filter( - build=item.build, - bom_item=item.bom_item - ).first() - - if line is None: - logger.warning(f"BuildLine does not exist for BuildItem {item.pk}") - n_missing += 1 - - if item.build is None or item.bom_item is None: - continue - - # Create one! - line = BuildLine.objects.create( - build=item.build, - bom_item=item.bom_item, - quantity=item.bom_item.quantity * item.build.quantity - ) - - # Link the BuildItem to the BuildLine - # In the next data migration, we remove the 'build' and 'bom_item' fields from BuildItem - item.build_line = line - item.save() - - if build_items.count() > 0: - logger.info(f"add_build_line_links: Updated {build_items.count()} BuildItem objects (added {n_missing})") - - -def reverse_build_links(apps, schema_editor): - """Reverse data migration from add_build_line_links - - Basically, iterate through each BuildItem and update the links based on the BuildLine - """ - - BuildItem = apps.get_model("build", "BuildItem") - - items = BuildItem.objects.all() - - for item in items: - item.build = item.build_line.build - item.bom_item = item.build_line.bom_item - item.save() - - if items.count() > 0: - logger.info(f"reverse_build_links: Updated {items.count()} BuildItem objects") - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0045_builditem_build_line'), - ] - - operations = [ - migrations.RunPython( - add_build_line_links, - reverse_code=reverse_build_links, - ) - ] diff --git a/src/backend/InvenTree/build/migrations/0047_auto_20230606_1058.py b/src/backend/InvenTree/build/migrations/0047_auto_20230606_1058.py deleted file mode 100644 index 88535c7347..0000000000 --- a/src/backend/InvenTree/build/migrations/0047_auto_20230606_1058.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 3.2.19 on 2023-06-06 10:58 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0101_stockitemtestresult_metadata'), - ('build', '0046_auto_20230606_1033'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='builditem', - unique_together={('build_line', 'stock_item', 'install_into')}, - ), - migrations.RemoveField( - model_name='builditem', - name='bom_item', - ), - migrations.RemoveField( - model_name='builditem', - name='build', - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0048_build_project_code.py b/src/backend/InvenTree/build/migrations/0048_build_project_code.py deleted file mode 100644 index fcc7153bc7..0000000000 --- a/src/backend/InvenTree/build/migrations/0048_build_project_code.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-14 09:22 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0019_projectcode_metadata'), - ('build', '0047_auto_20230606_1058'), - ] - - operations = [ - migrations.AddField( - model_name='build', - name='project_code', - field=models.ForeignKey(blank=True, help_text='Project code for this build order', null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.projectcode', verbose_name='Project Code'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0049_alter_builditem_build_line.py b/src/backend/InvenTree/build/migrations/0049_alter_builditem_build_line.py deleted file mode 100644 index 0f4a4796a9..0000000000 --- a/src/backend/InvenTree/build/migrations/0049_alter_builditem_build_line.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 4.2.12 on 2024-05-08 01:38 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0048_build_project_code'), - ] - - operations = [ - migrations.AlterField( - model_name='builditem', - name='build_line', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='build.buildline'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0050_auto_20240508_0138.py b/src/backend/InvenTree/build/migrations/0050_auto_20240508_0138.py deleted file mode 100644 index 11e2ef82ef..0000000000 --- a/src/backend/InvenTree/build/migrations/0050_auto_20240508_0138.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 4.2.12 on 2024-05-08 01:38 - -from django.db import migrations - - -def forward(apps, schema_editor): - """Find and delete any BuildItem instances which have a null BuildLine field.""" - - BuildItem = apps.get_model('build', 'BuildItem') - - items = BuildItem.objects.filter(build_line=None) - - if items.count() > 0: - print(f"Deleting {items.count()} BuildItem objects with null BuildLine field") - items.delete() - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0049_alter_builditem_build_line'), - ] - - operations = [ - migrations.RunPython(forward, reverse_code=migrations.RunPython.noop), - ] diff --git a/src/backend/InvenTree/build/migrations/0051_delete_buildorderattachment.py b/src/backend/InvenTree/build/migrations/0051_delete_buildorderattachment.py deleted file mode 100644 index d600bd240f..0000000000 --- a/src/backend/InvenTree/build/migrations/0051_delete_buildorderattachment.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 4.2.12 on 2024-06-09 09:02 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0050_auto_20240508_0138'), - ('common', '0026_auto_20240608_1238'), - ('company', '0069_company_active'), - ('order', '0099_alter_salesorder_status'), - ('part', '0123_parttesttemplate_choices'), - ('stock', '0110_alter_stockitemtestresult_finished_datetime_and_more') - ] - - operations = [ - migrations.DeleteModel( - name='BuildOrderAttachment', - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0052_build_status_custom_key_alter_build_status.py b/src/backend/InvenTree/build/migrations/0052_build_status_custom_key_alter_build_status.py deleted file mode 100644 index 3f3f77619f..0000000000 --- a/src/backend/InvenTree/build/migrations/0052_build_status_custom_key_alter_build_status.py +++ /dev/null @@ -1,50 +0,0 @@ -# Generated by Django 4.2.14 on 2024-08-07 22:40 - -import django.core.validators -from django.db import migrations - -import generic.states.fields -import generic.states.validators -import InvenTree.status_codes - - -class Migration(migrations.Migration): - - dependencies = [ - ("build", "0051_delete_buildorderattachment"), - ] - - operations = [ - migrations.AddField( - model_name="build", - name="status_custom_key", - field=generic.states.fields.ExtraInvenTreeCustomStatusModelField( - blank=True, - default=None, - help_text="Additional status information for this item", - null=True, - verbose_name="Custom status key", - validators=[ - generic.states.validators.CustomStatusCodeValidator( - status_class=InvenTree.status_codes.BuildStatus - ), - ] - ), - ), - migrations.AlterField( - model_name="build", - name="status", - field=generic.states.fields.InvenTreeCustomStatusModelField( - choices=InvenTree.status_codes.BuildStatus.items(), - default=10, - help_text="Build status code", - validators=[ - django.core.validators.MinValueValidator(0), - generic.states.validators.CustomStatusCodeValidator( - status_class=InvenTree.status_codes.BuildStatus - ), - ], - verbose_name="Build Status", - ), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0053_alter_build_part.py b/src/backend/InvenTree/build/migrations/0053_alter_build_part.py deleted file mode 100644 index d457b9bab2..0000000000 --- a/src/backend/InvenTree/build/migrations/0053_alter_build_part.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 4.2.16 on 2024-10-16 06:19 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0130_alter_parttesttemplate_part'), - ('build', '0052_build_status_custom_key_alter_build_status'), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='part', - field=models.ForeignKey(help_text='Select part to build', limit_choices_to={'assembly': True}, on_delete=django.db.models.deletion.CASCADE, related_name='builds', to='part.part', verbose_name='Part'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0054_build_start_date.py b/src/backend/InvenTree/build/migrations/0054_build_start_date.py deleted file mode 100644 index 1ef87e634c..0000000000 --- a/src/backend/InvenTree/build/migrations/0054_build_start_date.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.2.18 on 2025-01-20 02:00 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0053_alter_build_part'), - ] - - operations = [ - migrations.AddField( - model_name='build', - name='start_date', - field=models.DateField(blank=True, help_text='Scheduled start date for this build order', null=True, verbose_name='Build start date'), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0055_auto_20250221_1230.py b/src/backend/InvenTree/build/migrations/0055_auto_20250221_1230.py deleted file mode 100644 index 78ebe1d8ed..0000000000 --- a/src/backend/InvenTree/build/migrations/0055_auto_20250221_1230.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 4.2.19 on 2025-02-21 12:30 - -import InvenTree.fields -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("build", "0054_build_start_date"), - ] - - operations = [ - migrations.AlterField( - model_name='build', - name='link', - field=models.TextField( - null=True, blank=True - ) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0056_alter_build_link.py b/src/backend/InvenTree/build/migrations/0056_alter_build_link.py deleted file mode 100644 index 3d328d160f..0000000000 --- a/src/backend/InvenTree/build/migrations/0056_alter_build_link.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 4.2.19 on 2025-02-21 13:46 - -import InvenTree.fields -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("build", "0055_auto_20250221_1230"), - ] - - operations = [ - migrations.AlterField( - model_name="build", - name="link", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="Link to external URL", - max_length=2000, - verbose_name="External Link", - ), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0057_build_external.py b/src/backend/InvenTree/build/migrations/0057_build_external.py deleted file mode 100644 index 9ceb3a6378..0000000000 --- a/src/backend/InvenTree/build/migrations/0057_build_external.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 4.2.20 on 2025-03-13 22:34 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("build", "0056_alter_build_link"), - ] - - operations = [ - migrations.AddField( - model_name="build", - name="external", - field=models.BooleanField( - default=False, - help_text="This build order is fulfilled externally", - verbose_name="External Build", - ), - ), - ] diff --git a/src/backend/InvenTree/build/migrations/0058_buildline_consumed.py b/src/backend/InvenTree/build/migrations/0058_buildline_consumed.py deleted file mode 100644 index 70e6af9455..0000000000 --- a/src/backend/InvenTree/build/migrations/0058_buildline_consumed.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 4.2.15 on 2024-09-26 10:11 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0057_build_external'), - ] - - operations = [ - migrations.AddField( - model_name='buildline', - name='consumed', - field=models.DecimalField(decimal_places=5, default=0, help_text='Quantity of consumed stock', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Consumed'), - ), - ] diff --git a/src/backend/InvenTree/build/test_migrations.py b/src/backend/InvenTree/build/test_migrations.py index 6613c50dcc..14d1c6ec37 100644 --- a/src/backend/InvenTree/build/test_migrations.py +++ b/src/backend/InvenTree/build/test_migrations.py @@ -16,7 +16,13 @@ class TestForwardMigrations(MigratorTestCase): Part = self.old_state.apps.get_model('part', 'part') buildable_part = Part.objects.create( - name='Widget', description='Buildable Part', active=True + name='Widget', + description='Buildable Part', + active=True, + level=0, + tree_id=0, + lft=0, + rght=0, ) Build = self.old_state.apps.get_model('build', 'build') @@ -41,220 +47,3 @@ class TestForwardMigrations(MigratorTestCase): part.save() part.assembly = False part.save() - - -class TestReferenceMigration(MigratorTestCase): - """Test custom migration which adds 'reference' field to Build model.""" - - migrate_from = ('build', unit_test.getOldestMigrationFile('build')) - migrate_to = ('build', '0018_build_reference') - - def prepare(self): - """Create some builds.""" - Part = self.old_state.apps.get_model('part', 'part') - - part = Part.objects.create(name='Part', description='A test part') - - Build = self.old_state.apps.get_model('build', 'build') - - Build.objects.create(part=part, title='My very first build', quantity=10) - - Build.objects.create(part=part, title='My very second build', quantity=10) - - Build.objects.create(part=part, title='My very third build', quantity=10) - - # Ensure that the builds *do not* have a 'reference' field - for build in Build.objects.all(): - with self.assertRaises(AttributeError): - print(build.reference) - - def test_build_reference(self): - """Test that the build reference is correctly assigned to the PK of the Build.""" - Build = self.new_state.apps.get_model('build', 'build') - - self.assertEqual(Build.objects.count(), 3) - - # Check that the build reference is properly assigned - for build in Build.objects.all(): - self.assertEqual(str(build.reference), str(build.pk)) - - -class TestReferencePatternMigration(MigratorTestCase): - """Unit test for data migration which converts reference to new format. - - Ref: https://github.com/inventree/InvenTree/pull/3267 - """ - - migrate_from = ('build', '0019_auto_20201019_1302') - migrate_to = ('build', '0037_build_priority') - - def prepare(self): - """Create some initial data prior to migration.""" - Setting = self.old_state.apps.get_model('common', 'inventreesetting') - - # Create a custom existing prefix so we can confirm the operation is working - Setting.objects.create(key='BUILDORDER_REFERENCE_PREFIX', value='BuildOrder-') - - Part = self.old_state.apps.get_model('part', 'part') - - assembly = Part.objects.create( - name='Assy 1', description='An assembly', level=0, lft=0, rght=0, tree_id=0 - ) - - Build = self.old_state.apps.get_model('build', 'build') - - for idx in range(1, 11): - Build.objects.create( - part=assembly, - title=f'Build {idx}', - quantity=idx, - reference=f'{idx + 100}', - level=0, - lft=0, - rght=0, - tree_id=0, - ) - - def test_reference_migration(self): - """Test that the reference fields have been correctly updated.""" - Build = self.new_state.apps.get_model('build', 'build') - - for build in Build.objects.all(): - self.assertTrue(build.reference.startswith('BuildOrder-')) - - Setting = self.new_state.apps.get_model('common', 'inventreesetting') - - pattern = Setting.objects.get(key='BUILDORDER_REFERENCE_PATTERN') - - self.assertEqual(pattern.value, 'BuildOrder-{ref:04d}') - - -class TestBuildLineCreation(MigratorTestCase): - """Test that build lines are correctly created for existing builds. - - Ref: https://github.com/inventree/InvenTree/pull/4855 - - This PR added the 'BuildLine' model, which acts as a link between a Build and a BomItem. - - - Migration 0044 creates BuildLine objects for existing builds. - - Migration 0046 links any existing BuildItem objects to corresponding BuildLine - """ - - migrate_from = ('build', '0041_alter_build_title') - migrate_to = ('build', '0047_auto_20230606_1058') - - def prepare(self): - """Create data to work with.""" - # Model references - Part = self.old_state.apps.get_model('part', 'part') - BomItem = self.old_state.apps.get_model('part', 'bomitem') - Build = self.old_state.apps.get_model('build', 'build') - BuildItem = self.old_state.apps.get_model('build', 'builditem') - StockItem = self.old_state.apps.get_model('stock', 'stockitem') - - # The "BuildLine" model does not exist yet - with self.assertRaises(LookupError): - self.old_state.apps.get_model('build', 'buildline') - - # Create a part - assembly = Part.objects.create( - name='Assembly', - description='An assembly', - assembly=True, - level=0, - lft=0, - rght=0, - tree_id=0, - ) - - # Create components - for idx in range(1, 11): - part = Part.objects.create( - name=f'Part {idx}', - description=f'Part {idx}', - level=0, - lft=0, - rght=0, - tree_id=0, - ) - - # Create plentiful stock - StockItem.objects.create( - part=part, quantity=1000, level=0, lft=0, rght=0, tree_id=0 - ) - - # Create a BOM item - BomItem.objects.create( - part=assembly, sub_part=part, quantity=idx, reference=f'REF-{idx}' - ) - - # Create some builds - for idx in range(1, 4): - build = Build.objects.create( - part=assembly, - title=f'Build {idx}', - quantity=idx * 10, - reference=f'REF-{idx}', - level=0, - lft=0, - rght=0, - tree_id=0, - ) - - # Allocate stock to the build - for bom_item in BomItem.objects.all(): - stock_item = StockItem.objects.get(part=bom_item.sub_part) - BuildItem.objects.create( - build=build, - bom_item=bom_item, - stock_item=stock_item, - quantity=bom_item.quantity, - ) - - def test_build_line_creation(self): - """Test that the BuildLine objects have been created correctly.""" - Build = self.new_state.apps.get_model('build', 'build') - BomItem = self.new_state.apps.get_model('part', 'bomitem') - BuildLine = self.new_state.apps.get_model('build', 'buildline') - BuildItem = self.new_state.apps.get_model('build', 'builditem') - StockItem = self.new_state.apps.get_model('stock', 'stockitem') - - # There should be 3x builds - self.assertEqual(Build.objects.count(), 3) - - # 10x BOMItem objects - self.assertEqual(BomItem.objects.count(), 10) - - # 10x StockItem objects - self.assertEqual(StockItem.objects.count(), 10) - - # And 30x BuildLine items (1 for each BomItem for each Build) - self.assertEqual(BuildLine.objects.count(), 30) - - # And 30x BuildItem objects (1 for each BomItem for each Build) - self.assertEqual(BuildItem.objects.count(), 30) - - # Check that each BuildItem has been linked to a BuildLine - for item in BuildItem.objects.all(): - self.assertIsNotNone(item.build_line) - self.assertEqual(item.stock_item.part, item.build_line.bom_item.sub_part) - - item = BuildItem.objects.first() - - # Check that the "build" field has been removed - with self.assertRaises(AttributeError): - item.build - - # Check that the "bom_item" field has been removed - with self.assertRaises(AttributeError): - item.bom_item - - # Check that each BuildLine is correctly configured - for line in BuildLine.objects.all(): - # Check that the quantity is correct - self.assertEqual( - line.quantity, line.build.quantity * line.bom_item.quantity - ) - - # Check that the linked parts are correct - self.assertEqual(line.build.part, line.bom_item.part) diff --git a/src/backend/InvenTree/common/migrations/0001_initial.py b/src/backend/InvenTree/common/migrations/0001_initial.py deleted file mode 100644 index b9ac14acf6..0000000000 --- a/src/backend/InvenTree/common/migrations/0001_initial.py +++ /dev/null @@ -1,51 +0,0 @@ -# Generated by Django 2.2.4 on 2019-09-02 23:02 - -import django.core.validators -from django.db import migrations, models - - -class CreateModelOrSkip(migrations.CreateModel): - """Custom migration operation to create a model if it does not already exist. - - - If the model already exists, the migration is skipped - - This class has been added to deal with some errors being thrown in CI tests - - The 'common_currency' table doesn't exist anymore anyway! - - In the future, these migrations will be squashed - """ - - def database_forwards(self, app_label, schema_editor, from_state, to_state) -> None: - """Forwards migration *attempts* to create the model, but will fail gracefully if it already exists""" - - try: - super().database_forwards(app_label, schema_editor, from_state, to_state) - except Exception: # pragma: no cover - pass - - def state_forwards(self, app_label, state) -> None: - try: - super().state_forwards(app_label, state) - except Exception: # pragma: no cover - pass - - -class Migration(migrations.Migration): - - initial = True - atomic = False - - dependencies = [ - ] - - operations = [ - CreateModelOrSkip( - name='Currency', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('symbol', models.CharField(help_text='Currency Symbol e.g. $', max_length=10)), - ('suffix', models.CharField(help_text='Currency Suffix e.g. AUD', max_length=10, unique=True)), - ('description', models.CharField(help_text='Currency Description', max_length=100)), - ('value', models.DecimalField(decimal_places=5, help_text='Currency Value', max_digits=10, validators=[django.core.validators.MinValueValidator(1e-05), django.core.validators.MaxValueValidator(100000)])), - ('base', models.BooleanField(default=False, help_text='Use this currency as the base currency')), - ], - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0001_squashed_0007_colortheme.py b/src/backend/InvenTree/common/migrations/0001_squashed_0007_colortheme.py new file mode 100644 index 0000000000..07162fdd0a --- /dev/null +++ b/src/backend/InvenTree/common/migrations/0001_squashed_0007_colortheme.py @@ -0,0 +1,127 @@ +# Generated by Django 5.2.17 on 2026-09-09 23:26 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + replaces = [ + ("common", "0001_initial"), + ("common", "0002_auto_20190902_2304"), + ("common", "0003_auto_20190902_2310"), + ("common", "0004_inventreesetting"), + ("common", "0005_auto_20190915_1256"), + ("common", "0006_auto_20200203_0951"), + ("common", "0007_colortheme"), + ] + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="Currency", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "symbol", + models.CharField(help_text="Currency Symbol e.g. $", max_length=10), + ), + ( + "suffix", + models.CharField( + help_text="Currency Suffix e.g. AUD", max_length=10, unique=True + ), + ), + ( + "description", + models.CharField(help_text="Currency Description", max_length=100), + ), + ( + "value", + models.DecimalField( + decimal_places=5, + default=1.0, + help_text="Currency Value", + max_digits=10, + validators=[ + django.core.validators.MinValueValidator(1e-05), + django.core.validators.MaxValueValidator(100000), + ], + ), + ), + ( + "base", + models.BooleanField( + default=False, + help_text="Use this currency as the base currency", + ), + ), + ], + options={ + "verbose_name_plural": "Currencies", + }, + ), + migrations.CreateModel( + name="InvenTreeSetting", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "key", + models.CharField( + help_text="Settings key", max_length=50, unique=True + ), + ), + ( + "value", + models.CharField( + blank=True, help_text="Settings value", max_length=200 + ), + ), + ( + "description", + models.CharField( + blank=True, help_text="Settings description", max_length=200 + ), + ), + ], + options={ + "verbose_name": "InvenTree Setting", + "verbose_name_plural": "InvenTree Settings", + }, + ), + migrations.CreateModel( + name="ColorTheme", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(blank=True, default="", max_length=20)), + ("user", models.CharField(max_length=150, unique=True)), + ], + ), + ] diff --git a/src/backend/InvenTree/common/migrations/0002_auto_20190902_2304.py b/src/backend/InvenTree/common/migrations/0002_auto_20190902_2304.py deleted file mode 100644 index a8daf16139..0000000000 --- a/src/backend/InvenTree/common/migrations/0002_auto_20190902_2304.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.2.4 on 2019-09-02 23:04 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0001_initial'), - ] - - operations = [ - migrations.AlterModelOptions( - name='currency', - options={'verbose_name_plural': 'Currencies'}, - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0003_auto_20190902_2310.py b/src/backend/InvenTree/common/migrations/0003_auto_20190902_2310.py deleted file mode 100644 index 7bfdefa8c4..0000000000 --- a/src/backend/InvenTree/common/migrations/0003_auto_20190902_2310.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.4 on 2019-09-02 23:10 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0002_auto_20190902_2304'), - ] - - operations = [ - migrations.AlterField( - model_name='currency', - name='value', - field=models.DecimalField(decimal_places=5, default=1.0, help_text='Currency Value', max_digits=10, validators=[django.core.validators.MinValueValidator(1e-05), django.core.validators.MaxValueValidator(100000)]), - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0004_inventreesetting.py b/src/backend/InvenTree/common/migrations/0004_inventreesetting.py deleted file mode 100644 index 64fc9cd486..0000000000 --- a/src/backend/InvenTree/common/migrations/0004_inventreesetting.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 2.2.5 on 2019-09-15 12:44 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0003_auto_20190902_2310'), - ] - - operations = [ - migrations.CreateModel( - name='InvenTreeSetting', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('key', models.CharField(help_text='Settings key', max_length=50, unique=True)), - ('value', models.CharField(blank=True, help_text='Settings value', max_length=200)), - ], - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0005_auto_20190915_1256.py b/src/backend/InvenTree/common/migrations/0005_auto_20190915_1256.py deleted file mode 100644 index 2822fd82c3..0000000000 --- a/src/backend/InvenTree/common/migrations/0005_auto_20190915_1256.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 2.2.5 on 2019-09-15 12:56 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0004_inventreesetting'), - ] - - operations = [ - migrations.AddField( - model_name='inventreesetting', - name='description', - field=models.CharField(blank=True, help_text='Settings description', max_length=200), - ), - migrations.AlterField( - model_name='inventreesetting', - name='key', - field=models.CharField(help_text='Settings key', max_length=50, unique=True), - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0006_auto_20200203_0951.py b/src/backend/InvenTree/common/migrations/0006_auto_20200203_0951.py deleted file mode 100644 index 78460cfd62..0000000000 --- a/src/backend/InvenTree/common/migrations/0006_auto_20200203_0951.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.2.9 on 2020-02-03 09:51 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0005_auto_20190915_1256'), - ] - - operations = [ - migrations.AlterModelOptions( - name='inventreesetting', - options={'verbose_name': 'InvenTree Setting', 'verbose_name_plural': 'InvenTree Settings'}, - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0007_colortheme.py b/src/backend/InvenTree/common/migrations/0007_colortheme.py deleted file mode 100644 index 467e3e835f..0000000000 --- a/src/backend/InvenTree/common/migrations/0007_colortheme.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.0.7 on 2020-09-09 19:50 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0006_auto_20200203_0951'), - ] - - operations = [ - migrations.CreateModel( - name='ColorTheme', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(blank=True, default='', max_length=20)), - ('user', models.CharField(max_length=150, unique=True)), - ], - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0008_remove_inventreesetting_description.py b/src/backend/InvenTree/common/migrations/0008_remove_inventreesetting_description.py deleted file mode 100644 index d7889ced17..0000000000 --- a/src/backend/InvenTree/common/migrations/0008_remove_inventreesetting_description.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.0.7 on 2020-10-19 13:02 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0007_colortheme'), - ] - - operations = [ - migrations.RemoveField( - model_name='inventreesetting', - name='description', - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0008_remove_inventreesetting_description_squashed_0039_emailthread_emailmessage.py b/src/backend/InvenTree/common/migrations/0008_remove_inventreesetting_description_squashed_0039_emailthread_emailmessage.py new file mode 100644 index 0000000000..0ddef8b015 --- /dev/null +++ b/src/backend/InvenTree/common/migrations/0008_remove_inventreesetting_description_squashed_0039_emailthread_emailmessage.py @@ -0,0 +1,404 @@ +# Generated by Django 5.2.17 on 2026-09-08 23:18 + +import InvenTree.config +import InvenTree.fields +import InvenTree.models +import common.models +import common.validators +import django.db.models.deletion +import taggit.managers +import uuid +from django.conf import settings +from django.db import migrations, models +from moneyed import CURRENCIES + + +def set_default_currency(apps, schema_editor): + """Migrate the currency setting from config.yml to db.""" + from common.currency import currency_codes + from common.models import InvenTreeSetting + + base_currency = InvenTree.config.get_setting( + 'INVENTREE_BASE_CURRENCY', 'base_currency', 'USD' + ) + + if base_currency not in currency_codes(): + base_currency = currency_codes()[0] if currency_codes() else 'USD' + + InvenTreeSetting.set_setting( + 'INVENTREE_DEFAULT_CURRENCY', base_currency, None, create=True + ) + + +def set_currencies(apps, schema_editor): + """Set the default currency codes. + + Ref: https://github.com/inventree/InvenTree/pull/7390 + + Transfers any currency codes configured externally (config file / + environment variable) into the database settings model. + """ + InvenTreeSetting = apps.get_model('common', 'InvenTreeSetting') + + key = 'CURRENCY_CODES' + + codes = InvenTree.config.get_setting('INVENTREE_CURRENCIES', 'currencies', None) + + if codes is None: + return + + if isinstance(codes, str): + codes = codes.split(',') + + valid_codes = {code.strip().upper() for code in codes if code.strip().upper() in CURRENCIES} + + if len(valid_codes) == 0: + return + + value = ','.join(valid_codes) + + setting = InvenTreeSetting.objects.filter(key=key).first() + + if setting: + setting.value = value + setting.save() + else: + InvenTreeSetting(key=key, value=value).save() + + +class Migration(migrations.Migration): + + replaces = [('common', '0008_remove_inventreesetting_description'), ('common', '0009_delete_currency'), ('common', '0010_migrate_currency_setting'), ('common', '0011_auto_20210722_2114'), ('common', '0012_notificationentry'), ('common', '0013_webhookendpoint_webhookmessage'), ('common', '0014_notificationmessage'), ('common', '0015_newsfeedentry'), ('common', '0016_alter_notificationentry_updated'), ('common', '0017_notesimage'), ('common', '0018_projectcode'), ('common', '0019_projectcode_metadata'), ('common', '0020_customunit'), ('common', '0021_auto_20230805_1748'), ('common', '0022_projectcode_responsible'), ('common', '0023_auto_20240602_1332'), ('common', '0024_notesimage_model_id_notesimage_model_type'), ('common', '0025_attachment'), ('common', '0026_auto_20240608_1238'), ('common', '0027_alter_customunit_symbol'), ('common', '0028_colortheme_user_obj'), ('common', '0029_inventreecustomuserstatemodel'), ('common', '0030_barcodescanresult'), ('common', '0031_auto_20241026_0024'), ('common', '0032_selectionlist_selectionlistentry_and_more'), ('common', '0033_delete_colortheme'), ('common', '0034_alter_inventreecustomuserstatemodel_unique_together_and_more'), ('common', '0035_auto_20250221_1513'), ('common', '0036_alter_attachment_link'), ('common', '0037_dataoutput'), ('common', '0038_alter_attachment_model_type'), ('common', '0039_emailthread_emailmessage')] + + dependencies = [ + ('common', '0001_squashed_0007_colortheme'), + ('company', '0027_remove_supplierpricebreak_currency'), + ('contenttypes', '0002_remove_content_type_name'), + ('part', '0057_remove_partsellpricebreak_currency'), + ('plugin', '0009_alter_pluginconfig_key'), + ('users', '0010_alter_apitoken_key'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.RemoveField( + model_name='inventreesetting', + name='description', + ), + migrations.DeleteModel( + name='Currency', + ), + migrations.RunPython(code=set_default_currency, reverse_code=migrations.RunPython.noop), + migrations.CreateModel( + name='WebhookEndpoint', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('endpoint_id', models.CharField(default=uuid.uuid4, editable=False, help_text='Endpoint at which this webhook is received', max_length=255, verbose_name='Endpoint')), + ('name', models.CharField(blank=True, help_text='Name for this webhook', max_length=255, null=True, verbose_name='Name')), + ('active', models.BooleanField(default=True, help_text='Is this webhook active', verbose_name='Active')), + ('token', models.CharField(blank=True, default=uuid.uuid4, help_text='Token for access', max_length=255, null=True, verbose_name='Token')), + ('secret', models.CharField(blank=True, help_text='Shared secret for HMAC', max_length=255, null=True, verbose_name='Secret')), + ('user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + ), + migrations.CreateModel( + name='WebhookMessage', + fields=[ + ('message_id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='Unique identifier for this message', primary_key=True, serialize=False, verbose_name='Message ID')), + ('host', models.CharField(editable=False, help_text='Host from which this message was received', max_length=255, verbose_name='Host')), + ('header', models.CharField(blank=True, editable=False, help_text='Header of this message', max_length=255, null=True, verbose_name='Header')), + ('body', models.JSONField(blank=True, editable=False, help_text='Body of this message', null=True, verbose_name='Body')), + ('worked_on', models.BooleanField(default=False, help_text='Was the work on this message finished?', verbose_name='Worked on')), + ('endpoint', models.ForeignKey(blank=True, help_text='Endpoint on which this message was received', null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.webhookendpoint', verbose_name='Endpoint')), + ], + ), + migrations.CreateModel( + name='NotificationMessage', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('target_object_id', models.PositiveIntegerField()), + ('source_object_id', models.PositiveIntegerField(blank=True, null=True)), + ('category', models.CharField(max_length=250)), + ('name', models.CharField(max_length=250)), + ('message', models.CharField(blank=True, max_length=250, null=True)), + ('creation', models.DateTimeField(auto_now_add=True)), + ('read', models.BooleanField(default=False)), + ('source_content_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='notification_source', to='contenttypes.contenttype')), + ('target_content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notification_target', to='contenttypes.contenttype')), + ('user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + ), + migrations.CreateModel( + name='NewsFeedEntry', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('feed_id', models.CharField(max_length=250, unique=True, verbose_name='Id')), + ('title', models.CharField(max_length=250, verbose_name='Title')), + ('link', models.URLField(max_length=250, verbose_name='Link')), + ('published', models.DateTimeField(max_length=250, verbose_name='Published')), + ('author', models.CharField(max_length=250, verbose_name='Author')), + ('summary', models.CharField(max_length=250, verbose_name='Summary')), + ('read', models.BooleanField(default=False, help_text='Was this news item read?', verbose_name='Read')), + ], + ), + migrations.CreateModel( + name='NotificationEntry', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('key', models.CharField(max_length=250)), + ('uid', models.IntegerField()), + ('updated', models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated')), + ], + options={ + 'unique_together': {('key', 'uid')}, + }, + ), + migrations.CreateModel( + name='NotesImage', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('image', models.ImageField(help_text='Image file', upload_to=common.models.rename_notes_image, verbose_name='Image')), + ('date', models.DateTimeField(auto_now_add=True)), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='CustomUnit', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Unit name', max_length=50, unique=True, verbose_name='Name')), + ('symbol', models.CharField(blank=True, help_text='Optional unit symbol', max_length=10, unique=True, verbose_name='Symbol')), + ('definition', models.CharField(help_text='Unit definition', max_length=50, verbose_name='Definition')), + ], + options={ + 'verbose_name': 'Custom Unit', + }, + ), + migrations.AlterField( + model_name='inventreesetting', + name='value', + field=models.CharField(blank=True, help_text='Settings value', max_length=2000), + ), + migrations.CreateModel( + name='InvenTreeUserSetting', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('value', models.CharField(blank=True, help_text='Settings value', max_length=2000)), + ('key', models.CharField(help_text='Settings key', max_length=50)), + ('user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + options={ + 'verbose_name': 'InvenTree User Setting', + 'verbose_name_plural': 'InvenTree User Settings', + 'constraints': [models.UniqueConstraint(fields=('key', 'user'), name='unique key and user')], + }, + ), + migrations.CreateModel( + name='ProjectCode', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('code', models.CharField(help_text='Unique project code', max_length=50, unique=True, verbose_name='Project Code')), + ('description', models.CharField(blank=True, help_text='Project description', max_length=200, verbose_name='Description')), + ('metadata', models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata')), + ('responsible', models.ForeignKey(blank=True, help_text='User or group responsible for this project', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='project_codes', to='users.owner', verbose_name='Responsible')), + ], + options={ + 'verbose_name': 'Project Code', + }, + ), + migrations.RunPython(code=set_currencies, reverse_code=migrations.RunPython.noop), + migrations.AddField( + model_name='notesimage', + name='model_id', + field=models.IntegerField(blank=True, default=None, help_text='Target model ID for this image', null=True), + ), + migrations.AddField( + model_name='notesimage', + name='model_type', + field=models.CharField(blank=True, help_text='Target model type for this image', max_length=100, null=True), + ), + migrations.CreateModel( + name='Attachment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('model_id', models.PositiveIntegerField()), + ('attachment', models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to=common.models.rename_attachment, verbose_name='Attachment')), + ('link', InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=2000, null=True, verbose_name='Link')), + ('comment', models.CharField(blank=True, help_text='Attachment comment', max_length=250, verbose_name='Comment')), + ('upload_date', models.DateField(auto_now_add=True, help_text='Date the file was uploaded', null=True, verbose_name='Upload date')), + ('file_size', models.PositiveIntegerField(default=0, help_text='File size in bytes', verbose_name='File size')), + ('model_type', models.CharField(help_text='Target model type for this image', max_length=100, validators=[common.validators.validate_attachment_model_type])), + ('upload_user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User')), + ('metadata', models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata')), + ('tags', taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')), + ], + options={ + 'verbose_name': 'Attachment', + }, + bases=(InvenTree.models.PluginValidationMixin, models.Model), + ), + migrations.AlterField( + model_name='customunit', + name='symbol', + field=models.CharField(blank=True, help_text='Optional unit symbol', max_length=10, verbose_name='Symbol'), + ), + migrations.AddField( + model_name='colortheme', + name='user_obj', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.CreateModel( + name='InvenTreeCustomUserStateModel', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('key', models.IntegerField(help_text='Numerical value that will be saved in the models database', verbose_name='Value')), + ('name', models.CharField(help_text='Name of the state', max_length=250, validators=[common.validators.validate_uppercase, common.validators.validate_variable_string], verbose_name='Name')), + ('label', models.CharField(help_text='Label that will be displayed in the frontend', max_length=250, verbose_name='Label')), + ('color', models.CharField(choices=[('primary', 'primary'), ('secondary', 'secondary'), ('success', 'success'), ('danger', 'danger'), ('warning', 'warning'), ('info', 'info'), ('dark', 'dark')], default='secondary', help_text='Color that will be displayed in the frontend', max_length=10, verbose_name='Color')), + ('logical_key', models.IntegerField(help_text='State logical key that is equal to this custom state in business logic', verbose_name='Logical Key')), + ('reference_status', models.CharField(help_text='Status set that is extended with this custom state', max_length=250, verbose_name='Reference Status Set')), + ('model', models.ForeignKey(blank=True, help_text='Model this state is associated with', null=True, on_delete=django.db.models.deletion.SET_NULL, to='contenttypes.contenttype', verbose_name='Model')), + ], + options={ + 'verbose_name': 'Custom State', + 'verbose_name_plural': 'Custom States', + 'unique_together': {('reference_status', 'key'), ('reference_status', 'name')}, + }, + ), + migrations.CreateModel( + name='BarcodeScanResult', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('data', models.CharField(help_text='Barcode data', max_length=250, verbose_name='Data')), + ('timestamp', models.DateTimeField(auto_now_add=True, help_text='Date and time of the barcode scan', verbose_name='Timestamp')), + ('endpoint', models.CharField(blank=True, help_text='URL endpoint which processed the barcode', max_length=250, null=True, verbose_name='Path')), + ('context', models.JSONField(blank=True, help_text='Context data for the barcode scan', max_length=1000, null=True, verbose_name='Context')), + ('response', models.JSONField(blank=True, help_text='Response data from the barcode scan', max_length=1000, null=True, verbose_name='Response')), + ('result', models.BooleanField(default=False, help_text='Was the barcode scan successful?', verbose_name='Result')), + ('user', models.ForeignKey(blank=True, help_text='User who scanned the barcode', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + options={ + 'verbose_name': 'Barcode Scan', + }, + bases=(InvenTree.models.PluginValidationMixin, models.Model), + ), + migrations.CreateModel( + name='SelectionList', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('metadata', models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata')), + ('name', models.CharField(help_text='Name of the selection list', max_length=100, unique=True, verbose_name='Name')), + ('description', models.CharField(blank=True, help_text='Description of the selection list', max_length=250, verbose_name='Description')), + ('locked', models.BooleanField(default=False, help_text='Is this selection list locked?', verbose_name='Locked')), + ('active', models.BooleanField(default=True, help_text='Can this selection list be used?', verbose_name='Active')), + ('source_string', models.CharField(blank=True, help_text='Optional string identifying the source used for this list', max_length=1000, verbose_name='Source String')), + ('created', models.DateTimeField(auto_now_add=True, help_text='Date and time that the selection list was created', verbose_name='Created')), + ('last_updated', models.DateTimeField(auto_now=True, help_text='Date and time that the selection list was last updated', verbose_name='Last Updated')), + ], + options={ + 'verbose_name': 'Selection List', + 'verbose_name_plural': 'Selection Lists', + }, + bases=(InvenTree.models.PluginValidationMixin, models.Model), + ), + migrations.CreateModel( + name='SelectionListEntry', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('value', models.CharField(help_text='Value of the selection list entry', max_length=255, verbose_name='Value')), + ('label', models.CharField(help_text='Label for the selection list entry', max_length=255, verbose_name='Label')), + ('description', models.CharField(blank=True, help_text='Description of the selection list entry', max_length=250, verbose_name='Description')), + ('active', models.BooleanField(default=True, help_text='Is this selection list entry active?', verbose_name='Active')), + ('list', models.ForeignKey(blank=True, help_text='Selection list to which this entry belongs', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='entries', to='common.selectionlist', verbose_name='Selection List')), + ], + options={ + 'verbose_name': 'Selection List Entry', + 'verbose_name_plural': 'Selection List Entries', + 'unique_together': {('list', 'value')}, + }, + ), + migrations.AddField( + model_name='selectionlist', + name='default', + field=models.ForeignKey(blank=True, help_text='Default entry for this selection list', null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.selectionlistentry', verbose_name='Default Entry'), + ), + migrations.AddField( + model_name='selectionlist', + name='source_plugin', + field=models.ForeignKey(blank=True, help_text='Plugin which provides the selection list', null=True, on_delete=django.db.models.deletion.SET_NULL, to='plugin.pluginconfig', verbose_name='Source Plugin'), + ), + migrations.DeleteModel( + name='ColorTheme', + ), + migrations.AlterField( + model_name='attachment', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=2000, null=True, verbose_name='Link'), + ), + migrations.CreateModel( + name='DataOutput', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateField(auto_now_add=True)), + ('total', models.PositiveIntegerField(default=1)), + ('progress', models.PositiveIntegerField(default=0)), + ('complete', models.BooleanField(default=False)), + ('output_type', models.CharField(blank=True, max_length=100, null=True)), + ('template_name', models.CharField(blank=True, max_length=100, null=True)), + ('plugin', models.CharField(blank=True, max_length=100, null=True)), + ('output', models.FileField(blank=True, null=True, upload_to='data_output')), + ('errors', models.JSONField(blank=True, null=True)), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.AlterField( + model_name='attachment', + name='model_type', + field=models.CharField(help_text='Target model type for image', max_length=100, validators=[common.validators.validate_attachment_model_type], verbose_name='Model type'), + ), + migrations.CreateModel( + name='EmailThread', + fields=[ + ('metadata', models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata')), + ('key', models.CharField(blank=True, help_text='Unique key for this thread (used to identify the thread)', max_length=250, null=True, verbose_name='Key')), + ('global_id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='Unique identifier for this thread', primary_key=True, serialize=False, verbose_name='Global ID')), + ('started_internal', models.BooleanField(default=False, help_text='Was this thread started internally?', verbose_name='Started Internal')), + ('created', models.DateTimeField(auto_now_add=True, help_text='Date and time that the thread was created', verbose_name='Created')), + ('updated', models.DateTimeField(auto_now=True, help_text='Date and time that the thread was last updated', verbose_name='Updated')), + ], + options={ + 'verbose_name': 'Email Thread', + 'verbose_name_plural': 'Email Threads', + 'ordering': ['-updated'], + 'unique_together': {('key', 'global_id')}, + }, + bases=(InvenTree.models.PluginValidationMixin, models.Model), + ), + migrations.CreateModel( + name='EmailMessage', + fields=[ + ('global_id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='Unique identifier for this message', primary_key=True, serialize=False, unique=True, verbose_name='Global ID')), + ('message_id_key', models.CharField(blank=True, help_text='Identifier for this message (might be supplied by external system)', max_length=250, null=True, verbose_name='Message ID')), + ('thread_id_key', models.CharField(blank=True, help_text='Identifier for this message thread (might be supplied by external system)', max_length=250, null=True, verbose_name='Thread ID')), + ('subject', models.CharField(max_length=250)), + ('body', models.TextField()), + ('to', models.EmailField(max_length=254)), + ('sender', models.EmailField(max_length=254)), + ('status', models.CharField(blank=True, choices=[('A', 'Announced'), ('S', 'Sent'), ('F', 'Failed'), ('D', 'Delivered'), ('R', 'Read'), ('C', 'Confirmed')], max_length=50, null=True)), + ('timestamp', models.DateTimeField(auto_now_add=True)), + ('headers', models.JSONField(blank=True, null=True)), + ('full_message', models.TextField(blank=True, null=True)), + ('direction', models.CharField(blank=True, choices=[('I', 'Inbound'), ('O', 'Outbound')], max_length=50, null=True)), + ('priority', models.IntegerField(choices=[(0, 'None'), (1, 'Very High'), (2, 'High'), (3, 'Normal'), (4, 'Low'), (5, 'Very Low')], verbose_name='Priority')), + ('delivery_options', models.JSONField(blank=True, null=True)), + ('error_code', models.CharField(blank=True, max_length=50, null=True)), + ('error_message', models.TextField(blank=True, null=True)), + ('error_timestamp', models.DateTimeField(blank=True, null=True)), + ('thread', models.ForeignKey(blank=True, help_text='Linked thread for this message', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='messages', to='common.emailthread', verbose_name='Thread')), + ], + options={ + 'verbose_name': 'Email Message', + 'verbose_name_plural': 'Email Messages', + }, + ), + ] diff --git a/src/backend/InvenTree/common/migrations/0009_delete_currency.py b/src/backend/InvenTree/common/migrations/0009_delete_currency.py deleted file mode 100644 index 3e2edce4c7..0000000000 --- a/src/backend/InvenTree/common/migrations/0009_delete_currency.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-10 11:40 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0027_remove_supplierpricebreak_currency'), - ('part', '0057_remove_partsellpricebreak_currency'), - ('common', '0008_remove_inventreesetting_description'), - ] - - operations = [ - migrations.DeleteModel( - name='Currency', - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0010_migrate_currency_setting.py b/src/backend/InvenTree/common/migrations/0010_migrate_currency_setting.py deleted file mode 100644 index 8d84505f87..0000000000 --- a/src/backend/InvenTree/common/migrations/0010_migrate_currency_setting.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 3.2.4 on 2021-07-01 15:39 - -from django.db import migrations -from common.models import InvenTreeSetting -from InvenTree.config import get_setting - -def set_default_currency(apps, schema_editor): - """ migrate the currency setting from config.yml to db """ - # get value from settings-file - base_currency = get_setting('INVENTREE_BASE_CURRENCY', 'base_currency', 'USD') - - from common.currency import currency_codes - - # check if value is valid - if base_currency not in currency_codes(): - if len (currency_codes()) > 0: - base_currency = currency_codes()[0] - else: - base_currency = 'USD' - - # write to database - InvenTreeSetting.set_setting('INVENTREE_DEFAULT_CURRENCY', base_currency, None, create=True) - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0009_delete_currency'), - ] - - operations = [ - migrations.RunPython(set_default_currency, reverse_code=migrations.RunPython.noop), - ] diff --git a/src/backend/InvenTree/common/migrations/0011_auto_20210722_2114.py b/src/backend/InvenTree/common/migrations/0011_auto_20210722_2114.py deleted file mode 100644 index 2d7d28343f..0000000000 --- a/src/backend/InvenTree/common/migrations/0011_auto_20210722_2114.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 3.2.4 on 2021-07-22 21:14 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('common', '0010_migrate_currency_setting'), - ] - - operations = [ - migrations.CreateModel( - name='InvenTreeUserSetting', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('value', models.CharField(blank=True, help_text='Settings value', max_length=200)), - ('key', models.CharField(help_text='Settings key', max_length=50)), - ('user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User')), - ], - options={ - 'verbose_name': 'InvenTree User Setting', - 'verbose_name_plural': 'InvenTree User Settings', - }, - ), - migrations.AddConstraint( - model_name='inventreeusersetting', - constraint=models.UniqueConstraint(fields=('key', 'user'), name='unique key and user'), - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0012_notificationentry.py b/src/backend/InvenTree/common/migrations/0012_notificationentry.py deleted file mode 100644 index 77439c9f8c..0000000000 --- a/src/backend/InvenTree/common/migrations/0012_notificationentry.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 3.2.5 on 2021-11-03 13:54 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0011_auto_20210722_2114'), - ] - - operations = [ - migrations.CreateModel( - name='NotificationEntry', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('key', models.CharField(max_length=250)), - ('uid', models.IntegerField()), - ('updated', models.DateTimeField(auto_now=True)), - ], - options={ - 'unique_together': {('key', 'uid')}, - }, - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0013_webhookendpoint_webhookmessage.py b/src/backend/InvenTree/common/migrations/0013_webhookendpoint_webhookmessage.py deleted file mode 100644 index d926c8ef3f..0000000000 --- a/src/backend/InvenTree/common/migrations/0013_webhookendpoint_webhookmessage.py +++ /dev/null @@ -1,40 +0,0 @@ -# Generated by Django 3.2.5 on 2021-11-19 21:34 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import uuid - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('common', '0012_notificationentry'), - ] - - operations = [ - migrations.CreateModel( - name='WebhookEndpoint', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('endpoint_id', models.CharField(default=uuid.uuid4, editable=False, help_text='Endpoint at which this webhook is received', max_length=255, verbose_name='Endpoint')), - ('name', models.CharField(blank=True, help_text='Name for this webhook', max_length=255, null=True, verbose_name='Name')), - ('active', models.BooleanField(default=True, help_text='Is this webhook active', verbose_name='Active')), - ('token', models.CharField(blank=True, default=uuid.uuid4, help_text='Token for access', max_length=255, null=True, verbose_name='Token')), - ('secret', models.CharField(blank=True, help_text='Shared secret for HMAC', max_length=255, null=True, verbose_name='Secret')), - ('user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User')), - ], - ), - migrations.CreateModel( - name='WebhookMessage', - fields=[ - ('message_id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='Unique identifier for this message', primary_key=True, serialize=False, verbose_name='Message ID')), - ('host', models.CharField(editable=False, help_text='Host from which this message was received', max_length=255, verbose_name='Host')), - ('header', models.CharField(blank=True, editable=False, help_text='Header of this message', max_length=255, null=True, verbose_name='Header')), - ('body', models.JSONField(blank=True, editable=False, help_text='Body of this message', null=True, verbose_name='Body')), - ('worked_on', models.BooleanField(default=False, help_text='Was the work on this message finished?', verbose_name='Worked on')), - ('endpoint', models.ForeignKey(blank=True, help_text='Endpoint on which this message was received', null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.webhookendpoint', verbose_name='Endpoint')), - ], - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0014_notificationmessage.py b/src/backend/InvenTree/common/migrations/0014_notificationmessage.py deleted file mode 100644 index aa842a20a3..0000000000 --- a/src/backend/InvenTree/common/migrations/0014_notificationmessage.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 3.2.5 on 2022-02-13 03:09 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('contenttypes', '0002_remove_content_type_name'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('common', '0013_webhookendpoint_webhookmessage'), - ] - - operations = [ - migrations.CreateModel( - name='NotificationMessage', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('target_object_id', models.PositiveIntegerField()), - ('source_object_id', models.PositiveIntegerField(blank=True, null=True)), - ('category', models.CharField(max_length=250)), - ('name', models.CharField(max_length=250)), - ('message', models.CharField(blank=True, max_length=250, null=True)), - ('creation', models.DateTimeField(auto_now_add=True)), - ('read', models.BooleanField(default=False)), - ('source_content_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='notification_source', to='contenttypes.contenttype')), - ('target_content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notification_target', to='contenttypes.contenttype')), - ('user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User')), - ], - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0015_newsfeedentry.py b/src/backend/InvenTree/common/migrations/0015_newsfeedentry.py deleted file mode 100644 index b96ac7f0c1..0000000000 --- a/src/backend/InvenTree/common/migrations/0015_newsfeedentry.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 3.2.14 on 2022-07-31 19:13 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0014_notificationmessage'), - ] - - operations = [ - migrations.CreateModel( - name='NewsFeedEntry', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('feed_id', models.CharField(max_length=250, unique=True, verbose_name='Id')), - ('title', models.CharField(max_length=250, verbose_name='Title')), - ('link', models.URLField(max_length=250, verbose_name='Link')), - ('published', models.DateTimeField(max_length=250, verbose_name='Published')), - ('author', models.CharField(max_length=250, verbose_name='Author')), - ('summary', models.CharField(max_length=250, verbose_name='Summary')), - ('read', models.BooleanField(default=False, help_text='Was this news item read?', verbose_name='Read')), - ], - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0016_alter_notificationentry_updated.py b/src/backend/InvenTree/common/migrations/0016_alter_notificationentry_updated.py deleted file mode 100644 index 077fe4cb95..0000000000 --- a/src/backend/InvenTree/common/migrations/0016_alter_notificationentry_updated.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.16 on 2023-01-15 14:04 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0015_newsfeedentry'), - ] - - operations = [ - migrations.AlterField( - model_name='notificationentry', - name='updated', - field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'), - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0017_notesimage.py b/src/backend/InvenTree/common/migrations/0017_notesimage.py deleted file mode 100644 index 8c9aff2f6d..0000000000 --- a/src/backend/InvenTree/common/migrations/0017_notesimage.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-17 05:55 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - -import common.models - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('common', '0016_alter_notificationentry_updated'), - ] - - operations = [ - migrations.CreateModel( - name='NotesImage', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('image', models.ImageField(help_text='Image file', upload_to=common.models.rename_notes_image, verbose_name='Image')), - ('date', models.DateTimeField(auto_now_add=True)), - ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), - ], - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0018_projectcode.py b/src/backend/InvenTree/common/migrations/0018_projectcode.py deleted file mode 100644 index 544007012a..0000000000 --- a/src/backend/InvenTree/common/migrations/0018_projectcode.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-19 02:06 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0017_notesimage'), - ] - - operations = [ - migrations.CreateModel( - name='ProjectCode', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('code', models.CharField(help_text='Unique project code', max_length=50, unique=True, verbose_name='Project Code')), - ('description', models.CharField(blank=True, help_text='Project description', max_length=200, verbose_name='Description')), - ], - options={ - 'verbose_name': 'Project Code', - }, - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0019_projectcode_metadata.py b/src/backend/InvenTree/common/migrations/0019_projectcode_metadata.py deleted file mode 100644 index 8f3afe022a..0000000000 --- a/src/backend/InvenTree/common/migrations/0019_projectcode_metadata.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-19 13:38 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0018_projectcode'), - ] - - operations = [ - migrations.AddField( - model_name='projectcode', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0020_customunit.py b/src/backend/InvenTree/common/migrations/0020_customunit.py deleted file mode 100644 index 2c27252cf6..0000000000 --- a/src/backend/InvenTree/common/migrations/0020_customunit.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 3.2.20 on 2023-07-18 11:38 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0019_projectcode_metadata'), - ] - - operations = [ - migrations.CreateModel( - name='CustomUnit', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(help_text='Unit name', max_length=50, unique=True, verbose_name='Name')), - ('symbol', models.CharField(blank=True, help_text='Optional unit symbol', max_length=10, unique=True, verbose_name='Symbol')), - ('definition', models.CharField(help_text='Unit definition', max_length=50, verbose_name='Definition')), - ], - options={ - 'verbose_name': 'Custom Unit', - }, - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0021_auto_20230805_1748.py b/src/backend/InvenTree/common/migrations/0021_auto_20230805_1748.py deleted file mode 100644 index 66e553b144..0000000000 --- a/src/backend/InvenTree/common/migrations/0021_auto_20230805_1748.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.20 on 2023-08-05 17:48 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0020_customunit'), - ] - - operations = [ - migrations.AlterField( - model_name='inventreesetting', - name='value', - field=models.CharField(blank=True, help_text='Settings value', max_length=2000), - ), - migrations.AlterField( - model_name='inventreeusersetting', - name='value', - field=models.CharField(blank=True, help_text='Settings value', max_length=2000), - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0022_projectcode_responsible.py b/src/backend/InvenTree/common/migrations/0022_projectcode_responsible.py deleted file mode 100644 index a40a420f87..0000000000 --- a/src/backend/InvenTree/common/migrations/0022_projectcode_responsible.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.23 on 2023-11-20 08:04 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0010_alter_apitoken_key'), - ('common', '0021_auto_20230805_1748'), - ] - - operations = [ - migrations.AddField( - model_name='projectcode', - name='responsible', - field=models.ForeignKey(blank=True, help_text='User or group responsible for this project', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='project_codes', to='users.owner', verbose_name='Responsible'), - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0023_auto_20240602_1332.py b/src/backend/InvenTree/common/migrations/0023_auto_20240602_1332.py deleted file mode 100644 index 3c29bda31f..0000000000 --- a/src/backend/InvenTree/common/migrations/0023_auto_20240602_1332.py +++ /dev/null @@ -1,78 +0,0 @@ -# Generated by Django 4.2.12 on 2024-06-02 13:32 - -from django.conf import settings -from django.db import migrations - -from moneyed import CURRENCIES - -import InvenTree.config - - -def set_currencies(apps, schema_editor): - """Set the default currency codes. - - Ref: https://github.com/inventree/InvenTree/pull/7390 - - Previously, the allowed currency codes were set in the external configuration - (e.g via the configuration file or environment variables). - - Now, they are set in the database (via the InvenTreeSetting model). - - So, this data migration exists to transfer any configured currency codes, - from the external configuration, into the database settings model. - """ - - InvenTreeSetting = apps.get_model('common', 'InvenTreeSetting') - - key = 'CURRENCY_CODES' - - codes = InvenTree.config.get_setting('INVENTREE_CURRENCIES', 'currencies', None) - - if codes is None: - # No currency codes are defined in the configuration file - return - - if type(codes) == str: - codes = codes.split(',') - - valid_codes = set() - - for code in codes: - code = code.strip().upper() - - if code in CURRENCIES: - valid_codes.add(code) - - if len(valid_codes) == 0: - print(f"No currency codes found in configuration file - skipping migration") - return - - value = ','.join(valid_codes) - - if not settings.TESTING: # pragma: no cover - print(f"Found existing currency codes:", value) - - setting = InvenTreeSetting.objects.filter(key=key).first() - - if setting: - if not settings.TESTING: # pragma: no cover - print(f"- Updating existing setting for currency codes") - setting.value = value - setting.save() - else: - if not settings.TESTING: # pragma: no cover - print(f"- Creating new setting for currency codes") - setting = InvenTreeSetting(key=key, value=value) - setting.save() - - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0022_projectcode_responsible'), - ] - - operations = [ - migrations.RunPython(set_currencies, reverse_code=migrations.RunPython.noop) - ] diff --git a/src/backend/InvenTree/common/migrations/0024_notesimage_model_id_notesimage_model_type.py b/src/backend/InvenTree/common/migrations/0024_notesimage_model_id_notesimage_model_type.py deleted file mode 100644 index 681cd2633a..0000000000 --- a/src/backend/InvenTree/common/migrations/0024_notesimage_model_id_notesimage_model_type.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 4.2.12 on 2024-05-22 12:27 - -import common.validators -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0023_auto_20240602_1332'), - ] - - operations = [ - migrations.AddField( - model_name='notesimage', - name='model_id', - field=models.IntegerField(blank=True, default=None, help_text='Target model ID for this image', null=True), - ), - migrations.AddField( - model_name='notesimage', - name='model_type', - field=models.CharField(blank=True, null=True, help_text='Target model type for this image', max_length=100), - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0025_attachment.py b/src/backend/InvenTree/common/migrations/0025_attachment.py deleted file mode 100644 index 63f9ec69a6..0000000000 --- a/src/backend/InvenTree/common/migrations/0025_attachment.py +++ /dev/null @@ -1,43 +0,0 @@ -# Generated by Django 4.2.12 on 2024-06-08 12:37 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import taggit.managers - -import common.models -import common.validators -import InvenTree.fields -import InvenTree.models - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('contenttypes', '0002_remove_content_type_name'), - ('common', '0024_notesimage_model_id_notesimage_model_type'), - ] - - operations = [ - migrations.CreateModel( - name='Attachment', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('model_id', models.PositiveIntegerField()), - ('attachment', models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to=common.models.rename_attachment, verbose_name='Attachment')), - ('link', InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', null=True, verbose_name='Link')), - ('comment', models.CharField(blank=True, help_text='Attachment comment', max_length=250, verbose_name='Comment')), - ('upload_date', models.DateField(auto_now_add=True, help_text='Date the file was uploaded', null=True, verbose_name='Upload date')), - ('file_size', models.PositiveIntegerField(default=0, help_text='File size in bytes', verbose_name='File size')), - ('model_type', models.CharField(help_text='Target model type for this image', max_length=100, validators=[common.validators.validate_attachment_model_type])), - ('upload_user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User')), - ('metadata', models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata')), - ('tags', taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')) - ], - bases=(InvenTree.models.PluginValidationMixin, models.Model), - options={ - 'verbose_name': 'Attachment', - } - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0026_auto_20240608_1238.py b/src/backend/InvenTree/common/migrations/0026_auto_20240608_1238.py deleted file mode 100644 index 09e12e90dd..0000000000 --- a/src/backend/InvenTree/common/migrations/0026_auto_20240608_1238.py +++ /dev/null @@ -1,122 +0,0 @@ -# Generated by Django 4.2.12 on 2024-06-08 12:38 - -from django.db import migrations -from django.core.files.storage import default_storage - - -def get_legacy_models(): - """Return a set of legacy attachment models.""" - - # Legacy attachment types to convert: - # app_label, table name, target model, model ref - return [ - ('build', 'BuildOrderAttachment', 'build', 'build'), - ('company', 'CompanyAttachment', 'company', 'company'), - ('company', 'ManufacturerPartAttachment', 'manufacturerpart', 'manufacturer_part'), - ('order', 'PurchaseOrderAttachment', 'purchaseorder', 'order'), - ('order', 'SalesOrderAttachment', 'salesorder', 'order'), - ('order', 'ReturnOrderAttachment', 'returnorder', 'order'), - ('part', 'PartAttachment', 'part', 'part'), - ('stock', 'StockItemAttachment', 'stockitem', 'stock_item') - ] - - -def update_attachments(apps, schema_editor): - """Migrate any existing attachment models to the new attachment table.""" - - Attachment = apps.get_model('common', 'attachment') - - N = 0 - - for app, model, target_model, model_ref in get_legacy_models(): - LegacyAttachmentModel = apps.get_model(app, model) - - if LegacyAttachmentModel.objects.count() == 0: - continue - - to_create = [] - - for attachment in LegacyAttachmentModel.objects.all(): - - # Find the size of the file (if exists) - if attachment.attachment and default_storage.exists(attachment.attachment.name): - try: - file_size = default_storage.size(attachment.attachment.name) - except NotImplementedError: - file_size = 0 - else: - file_size = 0 - - to_create.append( - Attachment( - model_type=target_model, - model_id=getattr(attachment, model_ref).pk, - attachment=attachment.attachment, - link=attachment.link, - comment=attachment.comment, - upload_date=attachment.upload_date, - upload_user=attachment.user, - file_size=file_size - ) - ) - - if len(to_create) > 0: - print(f"Migrating {len(to_create)} attachments for the legacy '{model}' model.") - Attachment.objects.bulk_create(to_create) - - N += len(to_create) - - # Check the correct number of Attachment objects has been created - assert(N == Attachment.objects.count()) - - -def reverse_attachments(apps, schema_editor): - """Reverse data migration, and map new Attachment model back to legacy models.""" - - Attachment = apps.get_model('common', 'attachment') - - N = 0 - - for app, model, target_model, model_ref in get_legacy_models(): - LegacyAttachmentModel = apps.get_model(app, model) - - to_create = [] - - for attachment in Attachment.objects.filter(model_type=target_model): - - TargetModel = apps.get_model(app, target_model) - - data = { - 'attachment': attachment.attachment, - 'link': attachment.link, - 'comment': attachment.comment, - 'upload_date': attachment.upload_date, - 'user': attachment.upload_user, - model_ref: TargetModel.objects.get(pk=attachment.model_id) - } - - to_create.append(LegacyAttachmentModel(**data)) - - if len(to_create) > 0: - print(f"Reversing {len(to_create)} attachments for the legacy '{model}' model.") - LegacyAttachmentModel.objects.bulk_create(to_create) - - N += len(to_create) - - # Check the correct number of LegacyAttachmentModel objects has been created - assert(N == Attachment.objects.count()) - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0050_auto_20240508_0138'), - ('common', '0025_attachment'), - ('company', '0069_company_active'), - ('order', '0099_alter_salesorder_status'), - ('part', '0123_parttesttemplate_choices'), - ('stock', '0110_alter_stockitemtestresult_finished_datetime_and_more') - ] - - operations = [ - migrations.RunPython(update_attachments, reverse_code=reverse_attachments), - ] diff --git a/src/backend/InvenTree/common/migrations/0027_alter_customunit_symbol.py b/src/backend/InvenTree/common/migrations/0027_alter_customunit_symbol.py deleted file mode 100644 index a897773201..0000000000 --- a/src/backend/InvenTree/common/migrations/0027_alter_customunit_symbol.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.2.12 on 2024-07-04 10:32 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0026_auto_20240608_1238'), - ] - - operations = [ - migrations.AlterField( - model_name='customunit', - name='symbol', - field=models.CharField(blank=True, help_text='Optional unit symbol', max_length=10, verbose_name='Symbol'), - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0028_colortheme_user_obj.py b/src/backend/InvenTree/common/migrations/0028_colortheme_user_obj.py deleted file mode 100644 index 5e2ea45c01..0000000000 --- a/src/backend/InvenTree/common/migrations/0028_colortheme_user_obj.py +++ /dev/null @@ -1,39 +0,0 @@ -# Generated by Django 4.2.12 on 2024-07-04 10:23 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -def migrate_userthemes(apps, schema_editor): - """Mgrate text-based user references to ForeignKey references.""" - ColorTheme = apps.get_model("common", "ColorTheme") - User = apps.get_model(settings.AUTH_USER_MODEL) - - for theme in ColorTheme.objects.all(): - try: - theme.user_obj = User.objects.get(username=theme.user) - theme.save() - except User.DoesNotExist: - pass - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ("common", "0027_alter_customunit_symbol"), - ] - - operations = [ - migrations.AddField( - model_name="colortheme", - name="user_obj", - field=models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.CASCADE, - to=settings.AUTH_USER_MODEL, - ), - ), - migrations.RunPython(migrate_userthemes, migrations.RunPython.noop), - ] diff --git a/src/backend/InvenTree/common/migrations/0029_inventreecustomuserstatemodel.py b/src/backend/InvenTree/common/migrations/0029_inventreecustomuserstatemodel.py deleted file mode 100644 index 408edf6102..0000000000 --- a/src/backend/InvenTree/common/migrations/0029_inventreecustomuserstatemodel.py +++ /dev/null @@ -1,97 +0,0 @@ -# Generated by Django 4.2.14 on 2024-08-07 22:40 - -import django.db.models.deletion -from django.db import migrations, models - -from common.models import state_color_mappings - - -class Migration(migrations.Migration): - - dependencies = [ - ("contenttypes", "0002_remove_content_type_name"), - ("common", "0028_colortheme_user_obj"), - ] - - operations = [ - migrations.CreateModel( - name="InvenTreeCustomUserStateModel", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "key", - models.IntegerField( - help_text="Value that will be saved in the models database", - verbose_name="Key", - ), - ), - ( - "name", - models.CharField( - help_text="Name of the state", - max_length=250, - verbose_name="Name", - ), - ), - ( - "label", - models.CharField( - help_text="Label that will be displayed in the frontend", - max_length=250, - verbose_name="Label", - ), - ), - ( - "color", - models.CharField( - choices=state_color_mappings(), - default="secondary", - help_text="Color that will be displayed in the frontend", - max_length=10, - verbose_name="Color", - ), - ), - ( - "logical_key", - models.IntegerField( - help_text="State logical key that is equal to this custom state in business logic", - verbose_name="Logical Key", - ), - ), - ( - "reference_status", - models.CharField( - help_text="Status set that is extended with this custom state", - max_length=250, - verbose_name="Reference Status Set", - ), - ), - ( - "model", - models.ForeignKey( - blank=True, - help_text="Model this state is associated with", - null=True, - on_delete=django.db.models.deletion.SET_NULL, - to="contenttypes.contenttype", - verbose_name="Model", - ), - ), - ], - options={ - "verbose_name": "Custom State", - "verbose_name_plural": "Custom States", - "unique_together": { - ("model", "reference_status", "key", "logical_key") - }, - }, - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0030_barcodescanresult.py b/src/backend/InvenTree/common/migrations/0030_barcodescanresult.py deleted file mode 100644 index fe9ed14c6e..0000000000 --- a/src/backend/InvenTree/common/migrations/0030_barcodescanresult.py +++ /dev/null @@ -1,34 +0,0 @@ -# Generated by Django 4.2.15 on 2024-09-21 06:05 - -import InvenTree.models -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('common', '0029_inventreecustomuserstatemodel'), - ] - - operations = [ - migrations.CreateModel( - name='BarcodeScanResult', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('data', models.CharField(help_text='Barcode data', max_length=250, verbose_name='Data')), - ('timestamp', models.DateTimeField(auto_now_add=True, help_text='Date and time of the barcode scan', verbose_name='Timestamp')), - ('endpoint', models.CharField(blank=True, help_text='URL endpoint which processed the barcode', max_length=250, null=True, verbose_name='Path')), - ('context', models.JSONField(blank=True, help_text='Context data for the barcode scan', max_length=1000, null=True, verbose_name='Context')), - ('response', models.JSONField(blank=True, help_text='Response data from the barcode scan', max_length=1000, null=True, verbose_name='Response')), - ('result', models.BooleanField(default=False, help_text='Was the barcode scan successful?', verbose_name='Result')), - ('user', models.ForeignKey(blank=True, help_text='User who scanned the barcode', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User')), - ], - options={ - 'verbose_name': 'Barcode Scan', - }, - bases=(InvenTree.models.PluginValidationMixin, models.Model), - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0031_auto_20241026_0024.py b/src/backend/InvenTree/common/migrations/0031_auto_20241026_0024.py deleted file mode 100644 index 75b6c86a67..0000000000 --- a/src/backend/InvenTree/common/migrations/0031_auto_20241026_0024.py +++ /dev/null @@ -1,39 +0,0 @@ -# Generated by Django 4.2.16 on 2024-10-26 00:24 - -from django.conf import settings -from django.db import migrations - -import logging - -logger = logging.getLogger('inventree') - - -def update_news_feed_urls(apps, schema_editor): - """Update and validate the news feed URLs.""" - - from common.models import NewsFeedEntry - - n = 0 - - for entry in NewsFeedEntry.objects.all(): - if entry.link and entry.link.startswith('/'): - entry.link = settings.INVENTREE_BASE_URL + entry.link - entry.save() - n += 1 - - if n > 0: - logger.info("Updated link for %s NewsFeedEntry objects", n) - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0030_barcodescanresult'), - ] - - operations = [ - migrations.RunPython( - update_news_feed_urls, - reverse_code=migrations.RunPython.noop - ) - ] diff --git a/src/backend/InvenTree/common/migrations/0032_selectionlist_selectionlistentry_and_more.py b/src/backend/InvenTree/common/migrations/0032_selectionlist_selectionlistentry_and_more.py deleted file mode 100644 index 3db86b80b9..0000000000 --- a/src/backend/InvenTree/common/migrations/0032_selectionlist_selectionlistentry_and_more.py +++ /dev/null @@ -1,191 +0,0 @@ -# Generated by Django 4.2.16 on 2024-11-24 12:41 - -import django.db.models.deletion -from django.db import migrations, models - -import InvenTree.models - - -class Migration(migrations.Migration): - dependencies = [ - ('plugin', '0009_alter_pluginconfig_key'), - ('common', '0031_auto_20241026_0024'), - ] - - operations = [ - migrations.CreateModel( - name='SelectionList', - fields=[ - ( - 'id', - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name='ID', - ), - ), - ( - 'metadata', - models.JSONField( - blank=True, - help_text='JSON metadata field, for use by external plugins', - null=True, - verbose_name='Plugin Metadata', - ), - ), - ( - 'name', - models.CharField( - help_text='Name of the selection list', - max_length=100, - unique=True, - verbose_name='Name', - ), - ), - ( - 'description', - models.CharField( - blank=True, - help_text='Description of the selection list', - max_length=250, - verbose_name='Description', - ), - ), - ( - 'locked', - models.BooleanField( - default=False, - help_text='Is this selection list locked?', - verbose_name='Locked', - ), - ), - ( - 'active', - models.BooleanField( - default=True, - help_text='Can this selection list be used?', - verbose_name='Active', - ), - ), - ( - 'source_string', - models.CharField( - blank=True, - help_text='Optional string identifying the source used for this list', - max_length=1000, - verbose_name='Source String', - ), - ), - ( - 'created', - models.DateTimeField( - auto_now_add=True, - help_text='Date and time that the selection list was created', - verbose_name='Created', - ), - ), - ( - 'last_updated', - models.DateTimeField( - auto_now=True, - help_text='Date and time that the selection list was last updated', - verbose_name='Last Updated', - ), - ), - ], - options={ - 'verbose_name': 'Selection List', - 'verbose_name_plural': 'Selection Lists', - }, - bases=(InvenTree.models.PluginValidationMixin, models.Model), - ), - migrations.CreateModel( - name='SelectionListEntry', - fields=[ - ( - 'id', - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name='ID', - ), - ), - ( - 'value', - models.CharField( - help_text='Value of the selection list entry', - max_length=255, - verbose_name='Value', - ), - ), - ( - 'label', - models.CharField( - help_text='Label for the selection list entry', - max_length=255, - verbose_name='Label', - ), - ), - ( - 'description', - models.CharField( - blank=True, - help_text='Description of the selection list entry', - max_length=250, - verbose_name='Description', - ), - ), - ( - 'active', - models.BooleanField( - default=True, - help_text='Is this selection list entry active?', - verbose_name='Active', - ), - ), - ( - 'list', - models.ForeignKey( - blank=True, - help_text='Selection list to which this entry belongs', - null=True, - on_delete=django.db.models.deletion.CASCADE, - related_name='entries', - to='common.selectionlist', - verbose_name='Selection List', - ), - ), - ], - options={ - 'verbose_name': 'Selection List Entry', - 'verbose_name_plural': 'Selection List Entries', - 'unique_together': {('list', 'value')}, - }, - ), - migrations.AddField( - model_name='selectionlist', - name='default', - field=models.ForeignKey( - blank=True, - help_text='Default entry for this selection list', - null=True, - on_delete=django.db.models.deletion.SET_NULL, - to='common.selectionlistentry', - verbose_name='Default Entry', - ), - ), - migrations.AddField( - model_name='selectionlist', - name='source_plugin', - field=models.ForeignKey( - blank=True, - help_text='Plugin which provides the selection list', - null=True, - on_delete=django.db.models.deletion.SET_NULL, - to='plugin.pluginconfig', - verbose_name='Source Plugin', - ), - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0033_delete_colortheme.py b/src/backend/InvenTree/common/migrations/0033_delete_colortheme.py deleted file mode 100644 index 6f56515200..0000000000 --- a/src/backend/InvenTree/common/migrations/0033_delete_colortheme.py +++ /dev/null @@ -1,16 +0,0 @@ -# Generated by Django 4.2.17 on 2024-12-08 23:47 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("common", "0032_selectionlist_selectionlistentry_and_more"), - ] - - operations = [ - migrations.DeleteModel( - name="ColorTheme", - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0034_alter_inventreecustomuserstatemodel_unique_together_and_more.py b/src/backend/InvenTree/common/migrations/0034_alter_inventreecustomuserstatemodel_unique_together_and_more.py deleted file mode 100644 index b2eae1367a..0000000000 --- a/src/backend/InvenTree/common/migrations/0034_alter_inventreecustomuserstatemodel_unique_together_and_more.py +++ /dev/null @@ -1,32 +0,0 @@ -# Generated by Django 4.2.17 on 2024-12-27 09:15 - -import common.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0033_delete_colortheme'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='inventreecustomuserstatemodel', - unique_together=set(), - ), - migrations.AlterField( - model_name='inventreecustomuserstatemodel', - name='key', - field=models.IntegerField(help_text='Numerical value that will be saved in the models database', verbose_name='Value'), - ), - migrations.AlterField( - model_name='inventreecustomuserstatemodel', - name='name', - field=models.CharField(help_text='Name of the state', max_length=250, validators=[common.validators.validate_uppercase, common.validators.validate_variable_string], verbose_name='Name'), - ), - migrations.AlterUniqueTogether( - name='inventreecustomuserstatemodel', - unique_together={('reference_status', 'name'), ('reference_status', 'key')}, - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0035_auto_20250221_1513.py b/src/backend/InvenTree/common/migrations/0035_auto_20250221_1513.py deleted file mode 100644 index 99db26271a..0000000000 --- a/src/backend/InvenTree/common/migrations/0035_auto_20250221_1513.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 4.2.19 on 2025-02-21 15:13 - -import InvenTree.fields -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("common", "0034_alter_inventreecustomuserstatemodel_unique_together_and_more"), - ] - - operations = [migrations.AlterField( - model_name='attachment', - name='link', - field=models.TextField( - null=True, blank=True - ) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0036_alter_attachment_link.py b/src/backend/InvenTree/common/migrations/0036_alter_attachment_link.py deleted file mode 100644 index 688b6b89e5..0000000000 --- a/src/backend/InvenTree/common/migrations/0036_alter_attachment_link.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 4.2.19 on 2025-02-21 15:15 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("common", "0035_auto_20250221_1513"), - ] - - operations = [ - migrations.AlterField( - model_name="attachment", - name="link", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="Link to external URL", - max_length=2000, - null=True, - verbose_name="Link", - ), - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0037_dataoutput.py b/src/backend/InvenTree/common/migrations/0037_dataoutput.py deleted file mode 100644 index 72bf97eb1b..0000000000 --- a/src/backend/InvenTree/common/migrations/0037_dataoutput.py +++ /dev/null @@ -1,58 +0,0 @@ -# Generated by Django 4.2.19 on 2025-03-11 08:22 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ("common", "0036_alter_attachment_link"), - ] - - operations = [ - migrations.CreateModel( - name="DataOutput", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("created", models.DateField(auto_now_add=True)), - ("total", models.PositiveIntegerField(default=1)), - ("progress", models.PositiveIntegerField(default=0)), - ("complete", models.BooleanField(default=False)), - ( - "output_type", - models.CharField(blank=True, max_length=100, null=True), - ), - ( - "template_name", - models.CharField(blank=True, max_length=100, null=True), - ), - ("plugin", models.CharField(blank=True, max_length=100, null=True)), - ( - "output", - models.FileField(blank=True, null=True, upload_to="data_output"), - ), - ("errors", models.JSONField(blank=True, null=True)), - ( - "user", - models.ForeignKey( - blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0038_alter_attachment_model_type.py b/src/backend/InvenTree/common/migrations/0038_alter_attachment_model_type.py deleted file mode 100644 index f963689b83..0000000000 --- a/src/backend/InvenTree/common/migrations/0038_alter_attachment_model_type.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 4.2.20 on 2025-04-07 20:53 - -import common.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("common", "0037_dataoutput"), - ] - - operations = [ - migrations.AlterField( - model_name="attachment", - name="model_type", - field=models.CharField( - help_text="Target model type for image", - max_length=100, - validators=[common.validators.validate_attachment_model_type], - verbose_name="Model type", - ), - ), - ] diff --git a/src/backend/InvenTree/common/migrations/0039_emailthread_emailmessage.py b/src/backend/InvenTree/common/migrations/0039_emailthread_emailmessage.py deleted file mode 100644 index cf20c7d4d8..0000000000 --- a/src/backend/InvenTree/common/migrations/0039_emailthread_emailmessage.py +++ /dev/null @@ -1,61 +0,0 @@ -# Generated by Django 4.2.20 on 2025-05-07 00:17 - -import InvenTree.models -from django.db import migrations, models -import django.db.models.deletion -import uuid - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0038_alter_attachment_model_type'), - ] - - operations = [ - migrations.CreateModel( - name='EmailThread', - fields=[ - ('metadata', models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata')), - ('key', models.CharField(blank=True, help_text='Unique key for this thread (used to identify the thread)', max_length=250, null=True, verbose_name='Key')), - ('global_id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='Unique identifier for this thread', primary_key=True, serialize=False, verbose_name='Global ID')), - ('started_internal', models.BooleanField(default=False, help_text='Was this thread started internally?', verbose_name='Started Internal')), - ('created', models.DateTimeField(auto_now_add=True, help_text='Date and time that the thread was created', verbose_name='Created')), - ('updated', models.DateTimeField(auto_now=True, help_text='Date and time that the thread was last updated', verbose_name='Updated')), - ], - options={ - 'verbose_name': 'Email Thread', - 'verbose_name_plural': 'Email Threads', - 'ordering': ['-updated'], - 'unique_together': {('key', 'global_id')}, - }, - bases=(InvenTree.models.PluginValidationMixin, models.Model), - ), - migrations.CreateModel( - name='EmailMessage', - fields=[ - ('global_id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='Unique identifier for this message', primary_key=True, serialize=False, unique=True, verbose_name='Global ID')), - ('message_id_key', models.CharField(blank=True, help_text='Identifier for this message (might be supplied by external system)', max_length=250, null=True, verbose_name='Message ID')), - ('thread_id_key', models.CharField(blank=True, help_text='Identifier for this message thread (might be supplied by external system)', max_length=250, null=True, verbose_name='Thread ID')), - ('subject', models.CharField(max_length=250)), - ('body', models.TextField()), - ('to', models.EmailField(max_length=254)), - ('sender', models.EmailField(max_length=254)), - ('status', models.CharField(blank=True, choices=[('A', 'Announced'), ('S', 'Sent'), ('F', 'Failed'), ('D', 'Delivered'), ('R', 'Read'), ('C', 'Confirmed')], max_length=50, null=True)), - ('timestamp', models.DateTimeField(auto_now_add=True)), - ('headers', models.JSONField(blank=True, null=True)), - ('full_message', models.TextField(blank=True, null=True)), - ('direction', models.CharField(blank=True, choices=[('I', 'Inbound'), ('O', 'Outbound')], max_length=50, null=True)), - ('priority', models.IntegerField(choices=[(0, 'None'), (1, 'Very High'), (2, 'High'), (3, 'Normal'), (4, 'Low'), (5, 'Very Low')], verbose_name='Priority')), - ('delivery_options', models.JSONField(blank=True, null=True)), - ('error_code', models.CharField(blank=True, max_length=50, null=True)), - ('error_message', models.TextField(blank=True, null=True)), - ('error_timestamp', models.DateTimeField(blank=True, null=True)), - ('thread', models.ForeignKey(blank=True, help_text='Linked thread for this message', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='messages', to='common.emailthread', verbose_name='Thread')), - ], - options={ - 'verbose_name': 'Email Message', - 'verbose_name_plural': 'Email Messages', - }, - ), - ] diff --git a/src/backend/InvenTree/common/test_migrations.py b/src/backend/InvenTree/common/test_migrations.py index 9cea7d6aaa..8f5cbe676c 100644 --- a/src/backend/InvenTree/common/test_migrations.py +++ b/src/backend/InvenTree/common/test_migrations.py @@ -2,7 +2,6 @@ import importlib import io -import os from unittest import mock from django.core.files.base import ContentFile @@ -42,207 +41,6 @@ def get_historical_model(state, app: str, model: str): return model_class -def get_legacy_models(): - """Return a set of legacy attachment models.""" - # Legacy attachment types to convert: - # app_label, table name, target model, model ref - return [ - ('build', 'BuildOrderAttachment', 'build', 'build'), - ('company', 'CompanyAttachment', 'company', 'company'), - ( - 'company', - 'ManufacturerPartAttachment', - 'manufacturerpart', - 'manufacturer_part', - ), - ('order', 'PurchaseOrderAttachment', 'purchaseorder', 'order'), - ('order', 'SalesOrderAttachment', 'salesorder', 'order'), - ('order', 'ReturnOrderAttachment', 'returnorder', 'order'), - ('part', 'PartAttachment', 'part', 'part'), - ('stock', 'StockItemAttachment', 'stockitem', 'stock_item'), - ] - - -def generate_attachment(): - """Generate a file attachment object for test upload.""" - file_object = io.StringIO('Some dummy data') - file_object.seek(0) - - return ContentFile(file_object.getvalue(), 'test.txt') - - -class TestForwardMigrations(MigratorTestCase): - """Test entire schema migration sequence for the common app.""" - - migrate_from = ('common', '0024_notesimage_model_id_notesimage_model_type') - migrate_to = ('common', '0039_emailthread_emailmessage') - - def prepare(self): - """Create initial data. - - Legacy attachment model types are: - - BuildOrderAttachment - - CompanyAttachment - - ManufacturerPartAttachment - - PurchaseOrderAttachment - - SalesOrderAttachment - - ReturnOrderAttachment - - PartAttachment - - StockItemAttachment - """ - # Dummy MPPT data - tree = {'tree_id': 0, 'level': 0, 'lft': 0, 'rght': 0} - - # BuildOrderAttachment - Part = self.old_state.apps.get_model('part', 'Part') - Build = self.old_state.apps.get_model('build', 'Build') - - part = Part.objects.create( - name='Test Part', - description='Test Part Description', - active=True, - assembly=True, - purchaseable=True, - **tree, - ) - - build = Build.objects.create(part=part, title='Test Build', quantity=10, **tree) - - PartAttachment = self.old_state.apps.get_model('part', 'PartAttachment') - PartAttachment.objects.create( - part=part, attachment=generate_attachment(), comment='Test file attachment' - ) - PartAttachment.objects.create( - part=part, link='http://example.com', comment='Test link attachment' - ) - self.assertEqual(PartAttachment.objects.count(), 2) - - BuildOrderAttachment = self.old_state.apps.get_model( - 'build', 'BuildOrderAttachment' - ) - BuildOrderAttachment.objects.create( - build=build, link='http://example.com', comment='Test comment' - ) - BuildOrderAttachment.objects.create( - build=build, attachment=generate_attachment(), comment='a test file' - ) - self.assertEqual(BuildOrderAttachment.objects.count(), 2) - - StockItem = self.old_state.apps.get_model('stock', 'StockItem') - StockItemAttachment = self.old_state.apps.get_model( - 'stock', 'StockItemAttachment' - ) - - item = StockItem.objects.create(part=part, quantity=10, **tree) - - StockItemAttachment.objects.create( - stock_item=item, - attachment=generate_attachment(), - comment='Test file attachment', - ) - StockItemAttachment.objects.create( - stock_item=item, link='http://example.com', comment='Test link attachment' - ) - self.assertEqual(StockItemAttachment.objects.count(), 2) - - Company = self.old_state.apps.get_model('company', 'Company') - CompanyAttachment = self.old_state.apps.get_model( - 'company', 'CompanyAttachment' - ) - - company = Company.objects.create( - name='Test Company', - description='Test Company Description', - is_customer=True, - is_manufacturer=True, - is_supplier=True, - ) - - CompanyAttachment.objects.create( - company=company, - attachment=generate_attachment(), - comment='Test file attachment', - ) - CompanyAttachment.objects.create( - company=company, link='http://example.com', comment='Test link attachment' - ) - self.assertEqual(CompanyAttachment.objects.count(), 2) - - PurchaseOrder = self.old_state.apps.get_model('order', 'PurchaseOrder') - PurchaseOrderAttachment = self.old_state.apps.get_model( - 'order', 'PurchaseOrderAttachment' - ) - - po = PurchaseOrder.objects.create( - reference='PO-12345', - supplier=company, - description='Test Purchase Order Description', - ) - - PurchaseOrderAttachment.objects.create( - order=po, attachment=generate_attachment(), comment='Test file attachment' - ) - PurchaseOrderAttachment.objects.create( - order=po, link='http://example.com', comment='Test link attachment' - ) - self.assertEqual(PurchaseOrderAttachment.objects.count(), 2) - - SalesOrder = self.old_state.apps.get_model('order', 'SalesOrder') - SalesOrderAttachment = self.old_state.apps.get_model( - 'order', 'SalesOrderAttachment' - ) - - so = SalesOrder.objects.create( - reference='SO-12345', - customer=company, - description='Test Sales Order Description', - ) - - SalesOrderAttachment.objects.create( - order=so, attachment=generate_attachment(), comment='Test file attachment' - ) - SalesOrderAttachment.objects.create( - order=so, link='http://example.com', comment='Test link attachment' - ) - self.assertEqual(SalesOrderAttachment.objects.count(), 2) - - ReturnOrder = self.old_state.apps.get_model('order', 'ReturnOrder') - ReturnOrderAttachment = self.old_state.apps.get_model( - 'order', 'ReturnOrderAttachment' - ) - - ro = ReturnOrder.objects.create( - reference='RO-12345', - customer=company, - description='Test Return Order Description', - ) - - ReturnOrderAttachment.objects.create( - order=ro, attachment=generate_attachment(), comment='Test file attachment' - ) - ReturnOrderAttachment.objects.create( - order=ro, link='http://example.com', comment='Test link attachment' - ) - self.assertEqual(ReturnOrderAttachment.objects.count(), 2) - - def test_items_exist(self): - """Test to ensure that the attachments are correctly migrated.""" - Attachment = self.new_state.apps.get_model('common', 'Attachment') - - self.assertEqual(Attachment.objects.count(), 14) - - for model in [ - 'build', - 'company', - 'purchaseorder', - 'returnorder', - 'salesorder', - 'part', - 'stockitem', - ]: - self.assertEqual(Attachment.objects.filter(model_type=model).count(), 2) - - class TestAttachmentThumbnailMigration(MigratorTestCase): """Test that migration 0043 correctly populates is_image and generates thumbnails for existing attachments.""" @@ -684,55 +482,3 @@ class TestNoteMigrationBatching(MigratorTestCase): ) image = NotesImage.objects.get(pk=self.linked_image.pk) self.assertEqual(image.note.pk, expected_note.pk) - - -def prep_currency_migration(self, vals: str): - """Prepare the environment for the currency migration tests.""" - # Set keys - os.environ['INVENTREE_CURRENCIES'] = vals - - # And setting - InvenTreeSetting = self.old_state.apps.get_model('common', 'InvenTreeSetting') - - setting = InvenTreeSetting(key='CURRENCY_CODES', value='123') - setting.save() - - -class TestCurrencyMigration(MigratorTestCase): - """Test currency migration.""" - - migrate_from = ('common', '0022_projectcode_responsible') - migrate_to = ('common', '0023_auto_20240602_1332') - - def prepare(self): - """Prepare the environment for the migration test.""" - prep_currency_migration(self, 'USD,EUR,GBP') - - def test_currency_migration(self): - """Test that the currency migration works.""" - InvenTreeSetting = self.old_state.apps.get_model('common', 'InvenTreeSetting') - setting = InvenTreeSetting.objects.filter(key='CURRENCY_CODES').first() - - codes = str(setting.value).split(',') - - self.assertEqual(len(codes), 3) - - for code in ['USD', 'EUR', 'GBP']: - self.assertIn(code, codes) - - -class TestCurrencyMigrationNo(MigratorTestCase): - """Test currency migration.""" - - migrate_from = ('common', '0022_projectcode_responsible') - migrate_to = ('common', '0023_auto_20240602_1332') - - def prepare(self): - """Prepare the environment for the migration test.""" - prep_currency_migration(self, 'YYY,ZZZ') - - def test_currency_migration(self): - """Test that no currency migration occurs if wrong currencies are set.""" - InvenTreeSetting = self.old_state.apps.get_model('common', 'InvenTreeSetting') - setting = InvenTreeSetting.objects.filter(key='CURRENCY_CODES').first() - self.assertEqual(setting.value, '123') diff --git a/src/backend/InvenTree/company/migrations/0003_remove_supplierpart_minimum.py b/src/backend/InvenTree/company/migrations/0003_remove_supplierpart_minimum.py deleted file mode 100644 index 21ec25ebbf..0000000000 --- a/src/backend/InvenTree/company/migrations/0003_remove_supplierpart_minimum.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.2 on 2019-05-21 03:51 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0002_auto_20190520_2204'), - ] - - operations = [ - migrations.RemoveField( - model_name='supplierpart', - name='minimum', - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0003_remove_supplierpart_minimum_squashed_0047_supplierpart_pack_size.py b/src/backend/InvenTree/company/migrations/0003_remove_supplierpart_minimum_squashed_0047_supplierpart_pack_size.py new file mode 100644 index 0000000000..93e3360bd4 --- /dev/null +++ b/src/backend/InvenTree/company/migrations/0003_remove_supplierpart_minimum_squashed_0047_supplierpart_pack_size.py @@ -0,0 +1,399 @@ +# Generated by Django 5.2.17 on 2026-09-09 23:26 + +import InvenTree.fields +import InvenTree.validators +import common.currency +import company.models +import django.core.validators +import django.db.models.deletion +import djmoney.models.fields +import djmoney.models.validators +import stdimage.models +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + replaces = [('company', '0003_remove_supplierpart_minimum'), ('company', '0004_auto_20190525_2354'), ('company', '0005_auto_20190525_2356'), ('company', '0006_supplierpricebreak_currency'), ('company', '0007_remove_supplierpart_lead_time'), ('company', '0008_auto_20190913_1407'), ('company', '0009_auto_20191118_2323'), ('company', '0010_auto_20200201_1231'), ('company', '0011_auto_20200318_1114'), ('company', '0012_auto_20200318_1114'), ('company', '0013_auto_20200406_0131'), ('company', '0014_auto_20200407_0116'), ('company', '0015_company_is_manufacturer'), ('company', '0016_auto_20200412_2330'), ('company', '0017_auto_20200413_0320'), ('company', '0018_supplierpart_manufacturer'), ('company', '0019_auto_20200413_0642'), ('company', '0020_auto_20200413_0839'), ('company', '0021_remove_supplierpart_manufacturer_name'), ('company', '0022_auto_20200613_1045'), ('company', '0023_auto_20200808_0715'), ('company', '0024_unique_name_email_constraint'), ('company', '0025_auto_20201110_1001'), ('company', '0026_auto_20201110_1011'), ('company', '0027_remove_supplierpricebreak_currency'), ('company', '0028_remove_supplierpricebreak_cost'), ('company', '0029_company_currency'), ('company', '0030_auto_20201112_1112'), ('company', '0031_auto_20210103_2215'), ('company', '0032_auto_20210403_1837'), ('company', '0033_auto_20210410_1528'), ('company', '0034_manufacturerpart'), ('company', '0035_supplierpart_update_1'), ('company', '0036_supplierpart_update_2'), ('company', '0037_supplierpart_update_3'), ('company', '0038_manufacturerpartparameter'), ('company', '0039_auto_20210701_0509'), ('company', '0040_alter_company_currency'), ('company', '0041_alter_company_options'), ('company', '0042_supplierpricebreak_updated'), ('company', '0043_manufacturerpartattachment'), ('company', '0044_auto_20220607_2204'), ('company', '0045_alter_company_notes'), ('company', '0046_alter_company_image'), ('company', '0047_supplierpart_pack_size')] + + dependencies = [ + ('common', '0003_auto_20190902_2310'), + ('common', '0004_inventreesetting'), + ('company', '0002_auto_20190520_2204'), + ('part', '0045_auto_20200605_0932'), + ('part', '0060_merge_20201112_1722'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.RemoveField( + model_name='supplierpart', + name='minimum', + ), + migrations.AlterField( + model_name='supplierpart', + name='part', + field=models.ForeignKey(help_text='Select part', limit_choices_to={'is_template': False, 'purchaseable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.part'), + ), + migrations.AddField( + model_name='supplierpricebreak', + name='currency', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.currency'), + ), + migrations.RemoveField( + model_name='supplierpart', + name='lead_time', + ), + migrations.AlterField( + model_name='company', + name='notes', + field=models.TextField(blank=True), + ), + migrations.AlterField( + model_name='supplierpricebreak', + name='cost', + field=InvenTree.fields.RoundingDecimalField(decimal_places=5, max_digits=10, validators=[django.core.validators.MinValueValidator(0)]), + ), + migrations.AlterField( + model_name='supplierpricebreak', + name='quantity', + field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, max_digits=15, validators=[django.core.validators.MinValueValidator(1)]), + ), + migrations.RenameField( + model_name='company', + old_name='URL', + new_name='link', + ), + migrations.AlterField( + model_name='company', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external company information', max_length=2000), + ), + migrations.RenameField( + model_name='supplierpart', + old_name='URL', + new_name='link', + ), + migrations.AlterField( + model_name='supplierpart', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='URL for external supplier part link', max_length=2000), + ), + migrations.AlterField( + model_name='company', + name='image', + field=stdimage.models.StdImageField(blank=True, force_min_size=False, null=True, upload_to=company.models.rename_company_image, variations={}), + ), + migrations.AddField( + model_name='company', + name='is_manufacturer', + field=models.BooleanField(default=False, help_text='Does this company manufacture parts?'), + ), + migrations.RenameField( + model_name='supplierpart', + old_name='manufacturer', + new_name='manufacturer_name', + ), + migrations.AddField( + model_name='supplierpart', + name='manufacturer', + field=models.ForeignKey(blank=True, help_text='Select manufacturer', limit_choices_to={'is_manufacturer': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='manufactured_parts', to='company.company'), + ), + migrations.AlterField( + model_name='supplierpart', + name='supplier', + field=models.ForeignKey(help_text='Select supplier', limit_choices_to={'is_supplier': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplied_parts', to='company.company'), + ), + migrations.RemoveField( + model_name='supplierpart', + name='manufacturer_name', + ), + migrations.AlterField( + model_name='company', + name='address', + field=models.CharField(blank=True, help_text='Company address', max_length=200, verbose_name='Address'), + ), + migrations.AlterField( + model_name='company', + name='contact', + field=models.CharField(blank=True, help_text='Point of contact', max_length=100, verbose_name='Contact'), + ), + migrations.AlterField( + model_name='company', + name='description', + field=models.CharField(help_text='Description of the company', max_length=500, verbose_name='Company description'), + ), + migrations.AlterField( + model_name='company', + name='email', + field=models.EmailField(blank=True, help_text='Contact email address', max_length=254, verbose_name='Email'), + ), + migrations.AlterField( + model_name='company', + name='name', + field=models.CharField(help_text='Company name', max_length=100, unique=True, verbose_name='Company name'), + ), + migrations.AlterField( + model_name='company', + name='phone', + field=models.CharField(blank=True, help_text='Contact phone number', max_length=50, verbose_name='Phone number'), + ), + migrations.AlterField( + model_name='company', + name='website', + field=models.URLField(blank=True, help_text='Company website URL', verbose_name='Website'), + ), + migrations.AlterField( + model_name='supplierpart', + name='part', + field=models.ForeignKey(help_text='Select part', limit_choices_to={'is_template': False, 'purchaseable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.part', verbose_name='Base Part'), + ), + migrations.AlterModelOptions( + name='company', + options={'ordering': ['name'], 'verbose_name': 'Company'}, + ), + migrations.AlterField( + model_name='company', + name='email', + field=models.EmailField(blank=True, help_text='Contact email address', max_length=254, null=True, verbose_name='Email'), + ), + migrations.AlterField( + model_name='company', + name='name', + field=models.CharField(help_text='Company name', max_length=100, verbose_name='Company name'), + ), + migrations.AddConstraint( + model_name='company', + constraint=models.UniqueConstraint(fields=('name', 'email'), name='unique_name_email_pair'), + ), + migrations.AddField( + model_name='supplierpricebreak', + name='price', + field=djmoney.models.fields.MoneyField(decimal_places=4, default_currency='', help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price'), + ), + migrations.AddField( + model_name='supplierpricebreak', + name='price_currency', + field=djmoney.models.fields.CurrencyField(choices=[('AUD', 'AUD - Australian Dollar'), ('CAD', 'CAD - Canadian Dollar'), ('CNY', 'CNY - Chinese Yuan'), ('EUR', 'EUR - Euro'), ('GBP', 'GBP - British Pound'), ('JPY', 'JPY - Japanese Yen'), ('NZD', 'NZD - New Zealand Dollar'), ('USD', 'USD - US Dollar')], default='', editable=False, max_length=3), + ), + migrations.RemoveField( + model_name='supplierpricebreak', + name='currency', + ), + migrations.RemoveField( + model_name='supplierpricebreak', + name='cost', + ), + migrations.AddField( + model_name='company', + name='currency', + field=models.CharField(blank=True, help_text='Default currency used for this company', max_length=3, validators=[InvenTree.validators.validate_currency_code], verbose_name='Currency'), + ), + migrations.AlterField( + model_name='supplierpricebreak', + name='quantity', + field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Price break quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(1)], verbose_name='Quantity'), + ), + migrations.AlterField( + model_name='supplierpart', + name='MPN', + field=models.CharField(blank=True, help_text='Manufacturer part number', max_length=100, null=True, verbose_name='MPN'), + ), + migrations.AlterField( + model_name='supplierpart', + name='SKU', + field=models.CharField(help_text='Supplier stock keeping unit', max_length=100, verbose_name='SKU'), + ), + migrations.AlterField( + model_name='supplierpart', + name='description', + field=models.CharField(blank=True, help_text='Supplier part description', max_length=250, null=True, verbose_name='Description'), + ), + migrations.AlterField( + model_name='supplierpart', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='URL for external supplier part link', max_length=2000, null=True, verbose_name='Link'), + ), + migrations.AlterField( + model_name='supplierpart', + name='manufacturer', + field=models.ForeignKey(blank=True, help_text='Select manufacturer', limit_choices_to={'is_manufacturer': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='manufactured_parts', to='company.company', verbose_name='Manufacturer'), + ), + migrations.AlterField( + model_name='supplierpart', + name='note', + field=models.CharField(blank=True, help_text='Notes', max_length=100, null=True, verbose_name='Note'), + ), + migrations.AlterField( + model_name='supplierpart', + name='part', + field=models.ForeignKey(help_text='Select part', limit_choices_to={'purchaseable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.part', verbose_name='Base Part'), + ), + migrations.AlterField( + model_name='supplierpart', + name='supplier', + field=models.ForeignKey(help_text='Select supplier', limit_choices_to={'is_supplier': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplied_parts', to='company.company', verbose_name='Supplier'), + ), + migrations.AlterField( + model_name='company', + name='image', + field=stdimage.models.StdImageField(blank=True, force_min_size=False, null=True, upload_to=company.models.rename_company_image, variations={}, verbose_name='Image'), + ), + migrations.AlterField( + model_name='company', + name='is_customer', + field=models.BooleanField(default=False, help_text='Do you sell items to this company?', verbose_name='Is customer'), + ), + migrations.AlterField( + model_name='company', + name='is_manufacturer', + field=models.BooleanField(default=False, help_text='Does this company manufacture parts?', verbose_name='Is manufacturer'), + ), + migrations.AlterField( + model_name='company', + name='is_supplier', + field=models.BooleanField(default=True, help_text='Do you purchase items from this company?', verbose_name='Is supplier'), + ), + migrations.AlterField( + model_name='company', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external company information', max_length=2000, verbose_name='Link'), + ), + migrations.AlterField( + model_name='company', + name='notes', + field=models.TextField(blank=True, verbose_name='Notes'), + ), + migrations.AlterField( + model_name='supplierpart', + name='base_cost', + field=models.DecimalField(decimal_places=3, default=0, help_text='Minimum charge (e.g. stocking fee)', max_digits=10, validators=[django.core.validators.MinValueValidator(0)], verbose_name='base cost'), + ), + migrations.AlterField( + model_name='supplierpart', + name='multiple', + field=models.PositiveIntegerField(default=1, help_text='Order multiple', validators=[django.core.validators.MinValueValidator(1)], verbose_name='multiple'), + ), + migrations.AlterField( + model_name='supplierpart', + name='packaging', + field=models.CharField(blank=True, help_text='Part packaging', max_length=50, null=True, verbose_name='Packaging'), + ), + migrations.AlterField( + model_name='supplierpricebreak', + name='part', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pricebreaks', to='company.supplierpart', verbose_name='Part'), + ), + migrations.AlterField( + model_name='company', + name='description', + field=models.CharField(blank=True, help_text='Description of the company', max_length=500, verbose_name='Company description'), + ), + migrations.CreateModel( + name='ManufacturerPart', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('MPN', models.CharField(help_text='Manufacturer Part Number', max_length=100, null=True, verbose_name='MPN')), + ('link', InvenTree.fields.InvenTreeURLField(blank=True, help_text='URL for external manufacturer part link', max_length=2000, null=True, verbose_name='Link')), + ('description', models.CharField(blank=True, help_text='Manufacturer part description', max_length=250, null=True, verbose_name='Description')), + ('manufacturer', models.ForeignKey(help_text='Select manufacturer', limit_choices_to={'is_manufacturer': True}, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='manufactured_parts', to='company.company', verbose_name='Manufacturer')), + ('part', models.ForeignKey(help_text='Select part', limit_choices_to={'purchaseable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='manufacturer_parts', to='part.part', verbose_name='Base Part')), + ], + options={ + 'unique_together': {('part', 'manufacturer', 'MPN')}, + 'verbose_name': 'Manufacturer Part', + }, + ), + migrations.AddField( + model_name='supplierpart', + name='manufacturer_part', + field=models.ForeignKey(blank=True, help_text='Select manufacturer part', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='company.manufacturerpart', verbose_name='Manufacturer Part'), + ), + migrations.RemoveField( + model_name='supplierpart', + name='MPN', + ), + migrations.RemoveField( + model_name='supplierpart', + name='manufacturer', + ), + migrations.CreateModel( + name='ManufacturerPartParameter', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Parameter name', max_length=500, verbose_name='Name')), + ('value', models.CharField(help_text='Parameter value', max_length=500, verbose_name='Value')), + ('units', models.CharField(blank=True, help_text='Parameter units', max_length=64, null=True, verbose_name='Units')), + ('manufacturer_part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='parameters', to='company.manufacturerpart', verbose_name='Manufacturer Part')), + ], + options={ + 'verbose_name': 'Manufacturer Part Parameter', + 'unique_together': {('manufacturer_part', 'name')}, + }, + ), + migrations.AlterField( + model_name='supplierpricebreak', + name='price', + field=InvenTree.fields.InvenTreeModelMoneyField(currency_choices=[], decimal_places=4, default_currency=common.currency.currency_code_default, help_text='Unit price at specified quantity', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price'), + ), + migrations.AlterField( + model_name='supplierpricebreak', + name='price_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3), + ), + migrations.AlterField( + model_name='company', + name='currency', + field=models.CharField(blank=True, default=common.currency.currency_code_default, help_text='Default currency used for this company', max_length=3, validators=[InvenTree.validators.validate_currency_code], verbose_name='Currency'), + ), + migrations.AlterModelOptions( + name='company', + options={'ordering': ['name'], 'verbose_name': 'Company', 'verbose_name_plural': 'Companies'}, + ), + migrations.AddField( + model_name='supplierpricebreak', + name='updated', + field=models.DateTimeField(auto_now=True, null=True, verbose_name='last updated'), + ), + migrations.CreateModel( + name='ManufacturerPartAttachment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('attachment', models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to='attachments', verbose_name='Attachment')), + ('link', InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=2000, null=True, verbose_name='Link')), + ('comment', models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment')), + ('upload_date', models.DateField(auto_now_add=True, null=True, verbose_name='upload date')), + ('manufacturer_part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='company.manufacturerpart', verbose_name='Manufacturer Part')), + ('user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='supplierpart', + name='availability_updated', + field=models.DateTimeField(blank=True, help_text='Date of last update of availability data', null=True, verbose_name='Availability Updated'), + ), + migrations.AddField( + model_name='supplierpart', + name='available', + field=models.DecimalField(decimal_places=3, default=0, help_text='Quantity available from supplier', max_digits=10, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Available'), + ), + migrations.AlterField( + model_name='company', + name='notes', + field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Company Notes', max_length=50000, null=True, verbose_name='Notes'), + ), + migrations.AlterField( + model_name='company', + name='image', + field=stdimage.models.StdImageField(blank=True, force_min_size=False, null=True, upload_to=company.models.rename_company_image, variations={'preview': (256, 256), 'thumbnail': (128, 128)}, verbose_name='Image'), + ), + migrations.AddField( + model_name='supplierpart', + name='pack_size', + field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Unit quantity supplied in a single pack', max_digits=15, validators=[django.core.validators.MinValueValidator(0.001)], verbose_name='Pack Quantity'), + ), + ] diff --git a/src/backend/InvenTree/company/migrations/0004_auto_20190525_2354.py b/src/backend/InvenTree/company/migrations/0004_auto_20190525_2354.py deleted file mode 100644 index d402cdeed5..0000000000 --- a/src/backend/InvenTree/company/migrations/0004_auto_20190525_2354.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-25 13:54 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0003_remove_supplierpart_minimum'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpart', - name='part', - field=models.ForeignKey(help_text='Select part', limit_choices_to={'has_variants': False, 'purchaseable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.Part'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0005_auto_20190525_2356.py b/src/backend/InvenTree/company/migrations/0005_auto_20190525_2356.py deleted file mode 100644 index fe02be0d84..0000000000 --- a/src/backend/InvenTree/company/migrations/0005_auto_20190525_2356.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-25 13:56 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0004_auto_20190525_2354'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpart', - name='part', - field=models.ForeignKey(help_text='Select part', limit_choices_to={'is_template': False, 'purchaseable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.Part'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0006_supplierpricebreak_currency.py b/src/backend/InvenTree/company/migrations/0006_supplierpricebreak_currency.py deleted file mode 100644 index 94f533cf66..0000000000 --- a/src/backend/InvenTree/company/migrations/0006_supplierpricebreak_currency.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2.4 on 2019-09-02 23:34 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0003_auto_20190902_2310'), - ('company', '0005_auto_20190525_2356'), - ] - - operations = [ - migrations.AddField( - model_name='supplierpricebreak', - name='currency', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.Currency'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0007_remove_supplierpart_lead_time.py b/src/backend/InvenTree/company/migrations/0007_remove_supplierpart_lead_time.py deleted file mode 100644 index 5f050d5813..0000000000 --- a/src/backend/InvenTree/company/migrations/0007_remove_supplierpart_lead_time.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.2.5 on 2019-09-12 12:19 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0006_supplierpricebreak_currency'), - ] - - operations = [ - migrations.RemoveField( - model_name='supplierpart', - name='lead_time', - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0008_auto_20190913_1407.py b/src/backend/InvenTree/company/migrations/0008_auto_20190913_1407.py deleted file mode 100644 index d2e23da35f..0000000000 --- a/src/backend/InvenTree/company/migrations/0008_auto_20190913_1407.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2.5 on 2019-09-13 14:07 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0007_remove_supplierpart_lead_time'), - ] - - operations = [ - migrations.AlterField( - model_name='company', - name='URL', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external company information'), - ), - migrations.AlterField( - model_name='supplierpart', - name='URL', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='URL for external supplier part link'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0009_auto_20191118_2323.py b/src/backend/InvenTree/company/migrations/0009_auto_20191118_2323.py deleted file mode 100644 index 0ad6c682f2..0000000000 --- a/src/backend/InvenTree/company/migrations/0009_auto_20191118_2323.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.5 on 2019-11-18 23:23 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0008_auto_20190913_1407'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpricebreak', - name='quantity', - field=models.DecimalField(decimal_places=5, default=1, max_digits=15, validators=[django.core.validators.MinValueValidator(1)]), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0010_auto_20200201_1231.py b/src/backend/InvenTree/company/migrations/0010_auto_20200201_1231.py deleted file mode 100644 index c3839f68de..0000000000 --- a/src/backend/InvenTree/company/migrations/0010_auto_20200201_1231.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.9 on 2020-02-01 12:31 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0009_auto_20191118_2323'), - ] - - operations = [ - migrations.AlterField( - model_name='company', - name='notes', - field=models.TextField(blank=True), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0011_auto_20200318_1114.py b/src/backend/InvenTree/company/migrations/0011_auto_20200318_1114.py deleted file mode 100644 index cbfa73942b..0000000000 --- a/src/backend/InvenTree/company/migrations/0011_auto_20200318_1114.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2.9 on 2020-03-18 11:14 - -import InvenTree.fields -import django.core.validators -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0010_auto_20200201_1231'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpricebreak', - name='cost', - field=InvenTree.fields.RoundingDecimalField(decimal_places=5, max_digits=10, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0012_auto_20200318_1114.py b/src/backend/InvenTree/company/migrations/0012_auto_20200318_1114.py deleted file mode 100644 index ff99b91cab..0000000000 --- a/src/backend/InvenTree/company/migrations/0012_auto_20200318_1114.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2.9 on 2020-03-18 11:14 - -import InvenTree.fields -import django.core.validators -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0011_auto_20200318_1114'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpricebreak', - name='quantity', - field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, max_digits=15, validators=[django.core.validators.MinValueValidator(1)]), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0013_auto_20200406_0131.py b/src/backend/InvenTree/company/migrations/0013_auto_20200406_0131.py deleted file mode 100644 index 6cac8f7678..0000000000 --- a/src/backend/InvenTree/company/migrations/0013_auto_20200406_0131.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 2.2.10 on 2020-04-06 01:31 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0012_auto_20200318_1114'), - ] - - operations = [ - migrations.RenameField( - model_name='company', - old_name='URL', - new_name='link', - ), - migrations.RenameField( - model_name='supplierpart', - old_name='URL', - new_name='link', - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0014_auto_20200407_0116.py b/src/backend/InvenTree/company/migrations/0014_auto_20200407_0116.py deleted file mode 100644 index 03985a1ef3..0000000000 --- a/src/backend/InvenTree/company/migrations/0014_auto_20200407_0116.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2.10 on 2020-04-07 01:16 - -import company.models -from django.db import migrations -import stdimage.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0013_auto_20200406_0131'), - ] - - operations = [ - migrations.AlterField( - model_name='company', - name='image', - field=stdimage.models.StdImageField(blank=True, null=True, upload_to=company.models.rename_company_image), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0015_company_is_manufacturer.py b/src/backend/InvenTree/company/migrations/0015_company_is_manufacturer.py deleted file mode 100644 index b1e74667c2..0000000000 --- a/src/backend/InvenTree/company/migrations/0015_company_is_manufacturer.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.10 on 2020-04-12 23:21 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0014_auto_20200407_0116'), - ] - - operations = [ - migrations.AddField( - model_name='company', - name='is_manufacturer', - field=models.BooleanField(default=False, help_text='Does this company manufacture parts?'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0016_auto_20200412_2330.py b/src/backend/InvenTree/company/migrations/0016_auto_20200412_2330.py deleted file mode 100644 index cec6f5a219..0000000000 --- a/src/backend/InvenTree/company/migrations/0016_auto_20200412_2330.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.10 on 2020-04-12 23:30 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0015_company_is_manufacturer'), - ] - - operations = [ - migrations.AlterField( - model_name='company', - name='is_manufacturer', - field=models.BooleanField(default=False, help_text='Does this company manufacture parts?'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0017_auto_20200413_0320.py b/src/backend/InvenTree/company/migrations/0017_auto_20200413_0320.py deleted file mode 100644 index 0c13496713..0000000000 --- a/src/backend/InvenTree/company/migrations/0017_auto_20200413_0320.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.10 on 2020-04-13 03:20 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0016_auto_20200412_2330'), - ] - - operations = [ - migrations.RenameField( - model_name='supplierpart', - old_name='manufacturer', - new_name='manufacturer_name', - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0018_supplierpart_manufacturer.py b/src/backend/InvenTree/company/migrations/0018_supplierpart_manufacturer.py deleted file mode 100644 index 48c4461379..0000000000 --- a/src/backend/InvenTree/company/migrations/0018_supplierpart_manufacturer.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.10 on 2020-04-13 03:29 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0017_auto_20200413_0320'), - ] - - operations = [ - migrations.AddField( - model_name='supplierpart', - name='manufacturer', - field=models.ForeignKey(blank=True, help_text='Select manufacturer', limit_choices_to={'is_manufacturer': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='manufactured_parts', to='company.Company'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0019_auto_20200413_0642.py b/src/backend/InvenTree/company/migrations/0019_auto_20200413_0642.py deleted file mode 100644 index d4d33f9697..0000000000 --- a/src/backend/InvenTree/company/migrations/0019_auto_20200413_0642.py +++ /dev/null @@ -1,360 +0,0 @@ -# Generated by Django 2.2.10 on 2020-04-13 06:42 - -import sys -import os -from rapidfuzz import fuzz - -from django.db import migrations, connection -from django.db.utils import OperationalError, ProgrammingError - -""" -When this migration is tested by CI, it cannot accept user input. -So a simplified version of the migration is implemented. -""" -TESTING = 'test' in sys.argv - -def clear(): - if not TESTING: # pragma: no cover - os.system('cls' if os.name == 'nt' else 'clear') # ty:ignore[deprecated] - - -def reverse_association(apps, schema_editor): # pragma: no cover - """ - This is the 'reverse' operation of the manufacturer reversal. - This operation is easier: - - For each SupplierPart object, copy the name of the 'manufacturer' field - into the 'manufacturer_name' field. - """ - - cursor = connection.cursor() - - response = cursor.execute('select id, "MPN" from part_supplierpart;') - supplier_parts = cursor.fetchall() - - # Exit if there are no SupplierPart objects - # This crucial otherwise the unit test suite fails! - if len(supplier_parts) == 0: - return - - print("Reversing migration for manufacturer association") - - for (index, row) in enumerate(supplier_parts): - supplier_part_id, MPN = row - - print(f"Checking SupplierPart [{supplier_part_id}]:") - - # Grab the manufacturer ID from the part - response = cursor.execute(f"SELECT manufacturer_id FROM part_supplierpart WHERE id={supplier_part_id};") - - manufacturer_id = None - - row = cursor.fetchone() - - if row and len(row) > 0: - try: - manufacturer_id = int(row[0]) - except (TypeError, ValueError): - pass - - if manufacturer_id is None: - print(" - Manufacturer ID not set: Skipping") - continue - - print(" - Manufacturer ID: [{id}]".format(id=manufacturer_id)) - - # Now extract the "name" for the manufacturer - response = cursor.execute(f"SELECT name from company_company where id={manufacturer_id};") - - row = cursor.fetchone() - if row: - name = row[0] - - print(" - Manufacturer name: '{name}'".format(name=name)) - - response = cursor.execute("UPDATE part_supplierpart SET manufacturer_name='{name}' WHERE id={ID};".format(name=name, ID=supplier_part_id)) - -def associate_manufacturers(apps, schema_editor): - """ - This migration is the "middle step" in migration of the "manufacturer" field for the SupplierPart model. - - Previously the "manufacturer" field was a simple text field with the manufacturer name. - This is quite insufficient. - The new "manufacturer" field is a link to Company object which has the "is_manufacturer" parameter set to True - - This migration requires user interaction to create new "manufacturer" Company objects, - based on the text value in the "manufacturer_name" field (which was created in the previous migration). - - It uses fuzzy pattern matching to help the user out as much as possible. - """ - - def get_manufacturer_name(part_id): - """ - THIS IS CRITICAL! - - Once the pythonic representation of the model has removed the 'manufacturer_name' field, - it is NOT ACCESSIBLE by calling SupplierPart.manufacturer_name. - - However, as long as the migrations are applied in order, then the table DOES have a field called 'manufacturer_name'. - - So, we just need to request it using dirty SQL. - """ - - query = "SELECT manufacturer_name from part_supplierpart where id={ID};".format(ID=part_id) - - cursor = connection.cursor() - response = cursor.execute(query) - row = cursor.fetchone() - - if row and len(row) > 0: - return row[0] - return '' # pragma: no cover - - cursor = connection.cursor() - - response = cursor.execute(f'select id, "MPN" from part_supplierpart;') - supplier_parts = cursor.fetchall() - - # Exit if there are no SupplierPart objects - # This crucial otherwise the unit test suite fails! - if len(supplier_parts) == 0: - return - - # Link a 'manufacturer_name' to a 'Company' - links = {} - - # Map company names to company objects - companies = {} - - # Iterate through each company object - response = cursor.execute("select id, name from company_company;") - results = cursor.fetchall() - - for index, row in enumerate(results): - pk, name = row - - companies[name] = pk - - def link_part(part_id, name): - """ Attempt to link Part to an existing Company """ - - # Matches a company name directly - if name in companies.keys(): # pragma: no cover - print(" - Part[{pk}]: '{n}' maps to existing manufacturer".format(pk=part_id, n=name)) - - manufacturer_id = companies[name] - - query = f"update part_supplierpart set manufacturer_id={manufacturer_id} where id={part_id};" - result = cursor.execute(query) - - return True - - # Have we already mapped this - if name in links.keys(): # pragma: no cover - print(" - Part[{pk}]: Mapped '{n}' - manufacturer <{c}>".format(pk=part_id, n=name, c=links[name])) - - manufacturer_id = links[name] - - query = f"update part_supplierpart set manufacturer_id={manufacturer_id} where id={part_id};" - result = cursor.execute(query) - return True - - # Mapping not possible - return False - - def create_manufacturer(part_id, input_name, company_name): - """ Create a new manufacturer """ - - Company = apps.get_model('company', 'company') - - manufacturer = Company.objects.create( - name=company_name, - description=company_name, - is_manufacturer=True - ) - - # Map both names to the same company - links[input_name] = manufacturer.pk - links[company_name] = manufacturer.pk - - companies[company_name] = manufacturer.pk - - print(" - Part[{pk}]: Created new manufacturer: '{name}'".format(pk=part_id, name=company_name)) - - # Update SupplierPart object in the database - cursor.execute(f"update part_supplierpart set manufacturer_id={manufacturer.pk} where id={part_id};") - - def find_matches(text, threshold=65): - """ - Attempt to match a 'name' to an existing Company. - A list of potential matches will be returned. - """ - - matches = [] - - for name in companies.keys(): - # Case-insensitive matching - ratio = fuzz.partial_ratio(name.lower(), text.lower()) - - if ratio > threshold: # pragma: no cover - matches.append({'name': name, 'match': ratio}) - - if len(matches) > 0: # pragma: no cover - return [match['name'] for match in sorted(matches, key=lambda item: item['match'], reverse=True)] - else: - return [] - - - def map_part_to_manufacturer(part_id, idx, total): - - cursor = connection.cursor() - - name = get_manufacturer_name(part_id) - - # Skip empty names - if not name or len(name) == 0: # pragma: no cover - print(" - Part[{pk}]: No manufacturer_name provided, skipping".format(pk=part_id)) - return - - # Can be linked to an existing manufacturer - if link_part(part_id, name): # pragma: no cover - return - - # Find a list of potential matches - matches = find_matches(name) - - clear() - - # Present a list of options - if not TESTING: # pragma: no cover - print("----------------------------------") - - print("Checking part [{pk}] ({idx} of {total})".format(pk=part_id, idx=idx+1, total=total)) - - if not TESTING: # pragma: no cover - print("Manufacturer name: '{n}'".format(n=name)) - print("----------------------------------") - print("Select an option from the list below:") - - print("0) - Create new manufacturer '{n}'".format(n=name)) - print("") - - for i, m in enumerate(matches[:10]): - print("{i}) - Use manufacturer '{opt}'".format(i=i+1, opt=m)) - - print("") - print("OR - Type a new custom manufacturer name") - - while True: - if TESTING: - # When running unit tests, simply select the name of the part - response = '0' - else: # pragma: no cover - response = str(input("> ")).strip() - - # Attempt to parse user response as an integer - try: - n = int(response) - - # Option 0) is to create a new manufacturer with the current name - if n == 0: - - create_manufacturer(part_id, name, name) - return - - # Options 1) - n) select an existing manufacturer - else: # pragma: no cover - n = n - 1 - - if n < len(matches): - # Get the company which matches the selected options - company_name = matches[n] - company_id = companies[company_name] - - # Ensure the company is designated as a manufacturer - cursor.execute(f"update company_company set is_manufacturer=true where id={company_id};") - - # Link the company to the part - cursor.execute(f"update part_supplierpart set manufacturer_id={company_id} where id={part_id};") - - # Link the name to the company - links[name] = company_id - links[company_name] = company_id - - print(" - Part[{pk}]: Linked '{n}' to manufacturer '{m}'".format(pk=part_id, n=name, m=company_name)) - - return - else: - print("Please select a valid option") - - except ValueError: # pragma: no cover - # User has typed in a custom name! - - if not response or len(response) == 0: - # Response cannot be empty! - print("Please select an option") - - # Double-check if the typed name corresponds to an existing item - elif response in companies.keys(): - link_part(part_id, companies[response]) - return - - elif response in links.keys(): - link_part(part_id, links[response]) - return - - # No match, create a new manufacturer - else: - create_manufacturer(part_id, name, response) - return - - clear() - print("") - clear() - - if not TESTING: # pragma: no cover - print("---------------------------------------") - print("The SupplierPart model needs to be migrated,") - print("as the new 'manufacturer' field maps to a 'Company' reference.") - print("The existing 'manufacturer_name' field will be used to match") - print("against possible companies.") - print("This process requires user input.") - print("") - print("Note: This process MUST be completed to migrate the database.") - print("---------------------------------------") - print("") - - input("Press to continue.") - - clear() - - # Extract all SupplierPart objects from the database - cursor = connection.cursor() - response = cursor.execute('select id, "MPN", "SKU", manufacturer_id, manufacturer_name from part_supplierpart;') - results = cursor.fetchall() - - part_count = len(results) - - # Create a unique set of manufacturer names - for index, row in enumerate(results): - pk, MPN, SKU, manufacturer_id, manufacturer_name = row - - if manufacturer_id is not None: # pragma: no cover - print(f" - SupplierPart <{pk}> already has a manufacturer associated (skipping)") - continue - - map_part_to_manufacturer(pk, index, part_count) - - print("Done!") - -class Migration(migrations.Migration): - - atomic = False - - dependencies = [ - ('company', '0018_supplierpart_manufacturer'), - ] - - operations = [ - migrations.RunPython(associate_manufacturers, reverse_code=reverse_association) - ] diff --git a/src/backend/InvenTree/company/migrations/0020_auto_20200413_0839.py b/src/backend/InvenTree/company/migrations/0020_auto_20200413_0839.py deleted file mode 100644 index a3c7b9b64a..0000000000 --- a/src/backend/InvenTree/company/migrations/0020_auto_20200413_0839.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.10 on 2020-04-13 08:39 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0019_auto_20200413_0642'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpart', - name='supplier', - field=models.ForeignKey(help_text='Select supplier', limit_choices_to={'is_supplier': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplied_parts', to='company.Company'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0021_remove_supplierpart_manufacturer_name.py b/src/backend/InvenTree/company/migrations/0021_remove_supplierpart_manufacturer_name.py deleted file mode 100644 index f90a1372b9..0000000000 --- a/src/backend/InvenTree/company/migrations/0021_remove_supplierpart_manufacturer_name.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.2.10 on 2020-04-13 10:24 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0020_auto_20200413_0839'), - ] - - operations = [ - migrations.RemoveField( - model_name='supplierpart', - name='manufacturer_name', - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0022_auto_20200613_1045.py b/src/backend/InvenTree/company/migrations/0022_auto_20200613_1045.py deleted file mode 100644 index 66cd1231b8..0000000000 --- a/src/backend/InvenTree/company/migrations/0022_auto_20200613_1045.py +++ /dev/null @@ -1,55 +0,0 @@ -# Generated by Django 3.0.7 on 2020-06-13 10:45 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0045_auto_20200605_0932'), - ('company', '0021_remove_supplierpart_manufacturer_name'), - ] - - operations = [ - migrations.AlterField( - model_name='company', - name='address', - field=models.CharField(blank=True, help_text='Company address', max_length=200, verbose_name='Address'), - ), - migrations.AlterField( - model_name='company', - name='contact', - field=models.CharField(blank=True, help_text='Point of contact', max_length=100, verbose_name='Contact'), - ), - migrations.AlterField( - model_name='company', - name='description', - field=models.CharField(help_text='Description of the company', max_length=500, verbose_name='Company description'), - ), - migrations.AlterField( - model_name='company', - name='email', - field=models.EmailField(blank=True, help_text='Contact email address', max_length=254, verbose_name='Email'), - ), - migrations.AlterField( - model_name='company', - name='name', - field=models.CharField(help_text='Company name', max_length=100, unique=True, verbose_name='Company name'), - ), - migrations.AlterField( - model_name='company', - name='phone', - field=models.CharField(blank=True, help_text='Contact phone number', max_length=50, verbose_name='Phone number'), - ), - migrations.AlterField( - model_name='company', - name='website', - field=models.URLField(blank=True, help_text='Company website URL', verbose_name='Website'), - ), - migrations.AlterField( - model_name='supplierpart', - name='part', - field=models.ForeignKey(help_text='Select part', limit_choices_to={'is_template': False, 'purchaseable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.Part', verbose_name='Base Part'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0023_auto_20200808_0715.py b/src/backend/InvenTree/company/migrations/0023_auto_20200808_0715.py deleted file mode 100644 index d184108c9f..0000000000 --- a/src/backend/InvenTree/company/migrations/0023_auto_20200808_0715.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.0.7 on 2020-08-08 07:15 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0022_auto_20200613_1045'), - ] - - operations = [ - migrations.AlterModelOptions( - name='company', - options={'ordering': ['name'], 'verbose_name': 'Company'}, - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0024_unique_name_email_constraint.py b/src/backend/InvenTree/company/migrations/0024_unique_name_email_constraint.py deleted file mode 100644 index 1ac50c902b..0000000000 --- a/src/backend/InvenTree/company/migrations/0024_unique_name_email_constraint.py +++ /dev/null @@ -1,48 +0,0 @@ -from django.db import migrations, models - - -def reverse_empty_email(apps, schema_editor): # pragma: no cover - Company = apps.get_model('company', 'Company') - for company in Company.objects.all(): - if company.email == None: - company.email = '' - company.save() - - -def make_empty_email_field_null(apps, schema_editor): - Company = apps.get_model('company', 'Company') - for company in Company.objects.all(): - if company.email == '': - company.email = None - company.save() - - -class Migration(migrations.Migration): - - atomic = False - - dependencies = [ - ('company', '0023_auto_20200808_0715'), - ] - - operations = [ - # Allow email field to be NULL - migrations.AlterField( - model_name='company', - name='email', - field=models.EmailField(blank=True, help_text='Contact email address', max_length=254, null=True, unique=False, verbose_name='Email'), - ), - # Convert empty email string to NULL - migrations.RunPython(make_empty_email_field_null, reverse_code=reverse_empty_email), - # Remove unique constraint on name field - migrations.AlterField( - model_name='company', - name='name', - field=models.CharField(help_text='Company name', max_length=100, verbose_name='Company name'), - ), - # Add unique constraint on name/email pair - migrations.AddConstraint( - model_name='company', - constraint=models.UniqueConstraint(fields=('name', 'email'), name='unique_name_email_pair'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0025_auto_20201110_1001.py b/src/backend/InvenTree/company/migrations/0025_auto_20201110_1001.py deleted file mode 100644 index 0c11004e74..0000000000 --- a/src/backend/InvenTree/company/migrations/0025_auto_20201110_1001.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-10 10:01 - -from django.db import migrations, connection -import djmoney.models.fields -import common.currency - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0004_inventreesetting'), - ('company', '0024_unique_name_email_constraint'), - ] - - operations = [ - migrations.AddField( - model_name='supplierpricebreak', - name='price', - field=djmoney.models.fields.MoneyField(decimal_places=4, default_currency='', help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price'), - ), - migrations.AddField( - model_name='supplierpricebreak', - name='price_currency', - field=djmoney.models.fields.CurrencyField(choices=common.currency.currency_code_mappings(), default='', editable=False, max_length=3), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0026_auto_20201110_1011.py b/src/backend/InvenTree/company/migrations/0026_auto_20201110_1011.py deleted file mode 100644 index 4b1f155458..0000000000 --- a/src/backend/InvenTree/company/migrations/0026_auto_20201110_1011.py +++ /dev/null @@ -1,154 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-10 10:11 - -import logging -import sys - -from moneyed import CURRENCIES -from django.db import migrations, connection -from company.models import SupplierPriceBreak - - -logger = logging.getLogger('inventree') - - -def migrate_currencies(apps, schema_editor): - """ - Migrate from the 'old' method of handling currencies, - to the new method which uses the django-money library. - - Previously, we created a custom Currency model, - which was very simplistic. - - Here we will attempt to map each existing "currency" reference - for the SupplierPriceBreak model, to a new django-money compatible currency. - """ - - logger.debug("Updating currency references for SupplierPriceBreak model...") - - # A list of available currency codes - currency_codes = CURRENCIES.keys() - - cursor = connection.cursor() - - # The 'suffix' field denotes the currency code - response = cursor.execute('SELECT id, suffix, description from common_currency;') - - results = cursor.fetchall() - - remap = {} - - for index, row in enumerate(results): - pk, suffix, description = row - - suffix = suffix.strip().upper() - - if suffix not in currency_codes: # pragma: no cover - logger.warning(f"Missing suffix: '{suffix}'") - - while suffix not in currency_codes: - # Ask the user to input a valid currency - print(f"Could not find a valid currency matching '{suffix}'.") - print("Please enter a valid currency code") - suffix = str(input("> ")).strip() - - if pk not in remap.keys(): - remap[pk] = suffix - - # Now iterate through each SupplierPriceBreak and update the rows - response = cursor.execute('SELECT id, cost, currency_id, price, price_currency from part_supplierpricebreak;') - - results = cursor.fetchall() - - count = 0 - - for index, row in enumerate(results): - pk, cost, currency_id, price, price_currency = row - - # Copy the 'cost' field across to the 'price' field - response = cursor.execute(f'UPDATE part_supplierpricebreak set price={cost} where id={pk};') - - # Extract the updated currency code - currency_code = remap.get(currency_id, 'USD') - - # Update the currency code - response = cursor.execute(f"UPDATE part_supplierpricebreak set price_currency= '{currency_code}' where id={pk};") - - count += 1 - - if count > 0: - logger.info(f"Updated {count} SupplierPriceBreak rows") - -def reverse_currencies(apps, schema_editor): # pragma: no cover - """ - Reverse the "update" process. - - Here we may be in the situation that the legacy "Currency" table is empty, - and so we have to re-populate it based on the new price_currency codes. - """ - - print("Reversing currency migration...") - - cursor = connection.cursor() - - # Extract a list of currency codes which are in use - response = cursor.execute(f'SELECT id, price, price_currency from part_supplierpricebreak;') - - results = cursor.fetchall() - - codes_in_use = set() - - for index, row in enumerate(results): - pk, price, code = row - - codes_in_use.add(code) - - # Copy the 'price' field back into the 'cost' field - response = cursor.execute(f'UPDATE part_supplierpricebreak set cost={price} where id={pk};') - - # Keep a dict of which currency objects map to which code - code_map = {} - - # For each currency code in use, check if we have a matching Currency object - for code in codes_in_use: - response = cursor.execute(f"SELECT id, suffix from common_currency where suffix='{code}';") - row = cursor.fetchone() - - if row is not None: - # A match exists! - pk, suffix = row - code_map[suffix] = pk - else: - # No currency object exists! - description = CURRENCIES[code] - - # Create a new object in the database - print(f"Creating new Currency object for {code}") - - # Construct a query to create a new Currency object - query = f'INSERT into common_currency (symbol, suffix, description, value, base) VALUES ("$", "{code}", "{description}", 1.0, False);' - - response = cursor.execute(query) - - code_map[code] = cursor.lastrowid - - # Ok, now we know how each suffix maps to a Currency object - for suffix in code_map.keys(): - pk = code_map[suffix] - - # Update the table to point to the Currency objects - print(f"Currency {suffix} -> pk {pk}") - - response = cursor.execute(f"UPDATE part_supplierpricebreak set currency_id={pk} where price_currency='{suffix}';") - - -class Migration(migrations.Migration): - - atomic = False - - dependencies = [ - ('company', '0025_auto_20201110_1001'), - ] - - operations = [ - migrations.RunPython(migrate_currencies, reverse_code=reverse_currencies), - ] diff --git a/src/backend/InvenTree/company/migrations/0027_remove_supplierpricebreak_currency.py b/src/backend/InvenTree/company/migrations/0027_remove_supplierpricebreak_currency.py deleted file mode 100644 index b2e23d7538..0000000000 --- a/src/backend/InvenTree/company/migrations/0027_remove_supplierpricebreak_currency.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-10 11:40 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0026_auto_20201110_1011'), - ] - - operations = [ - migrations.RemoveField( - model_name='supplierpricebreak', - name='currency', - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0028_remove_supplierpricebreak_cost.py b/src/backend/InvenTree/company/migrations/0028_remove_supplierpricebreak_cost.py deleted file mode 100644 index 0522560f26..0000000000 --- a/src/backend/InvenTree/company/migrations/0028_remove_supplierpricebreak_cost.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-10 11:42 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0027_remove_supplierpricebreak_currency'), - ] - - operations = [ - migrations.RemoveField( - model_name='supplierpricebreak', - name='cost', - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0029_company_currency.py b/src/backend/InvenTree/company/migrations/0029_company_currency.py deleted file mode 100644 index fda3c15e96..0000000000 --- a/src/backend/InvenTree/company/migrations/0029_company_currency.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-11 23:22 - -import InvenTree.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0028_remove_supplierpricebreak_cost'), - ] - - operations = [ - migrations.AddField( - model_name='company', - name='currency', - field=models.CharField(blank=True, help_text='Default currency used for this company', max_length=3, validators=[InvenTree.validators.validate_currency_code], verbose_name='Currency'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0030_auto_20201112_1112.py b/src/backend/InvenTree/company/migrations/0030_auto_20201112_1112.py deleted file mode 100644 index 367ed303e5..0000000000 --- a/src/backend/InvenTree/company/migrations/0030_auto_20201112_1112.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-12 00:12 - -import InvenTree.fields -import django.core.validators -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0029_company_currency'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpricebreak', - name='quantity', - field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Price break quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(1)], verbose_name='Quantity'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0031_auto_20210103_2215.py b/src/backend/InvenTree/company/migrations/0031_auto_20210103_2215.py deleted file mode 100644 index a8ff0d9d63..0000000000 --- a/src/backend/InvenTree/company/migrations/0031_auto_20210103_2215.py +++ /dev/null @@ -1,61 +0,0 @@ -# Generated by Django 3.0.7 on 2021-01-03 11:15 - -import InvenTree.fields -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0060_merge_20201112_1722'), - ('company', '0030_auto_20201112_1112'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpart', - name='MPN', - field=models.CharField(blank=True, help_text='Manufacturer part number', max_length=100, null=True, verbose_name='MPN'), - ), - migrations.AlterField( - model_name='supplierpart', - name='SKU', - field=models.CharField(help_text='Supplier stock keeping unit', max_length=100, verbose_name='SKU'), - ), - migrations.AlterField( - model_name='supplierpart', - name='description', - field=models.CharField(blank=True, help_text='Supplier part description', max_length=250, null=True, verbose_name='Description'), - ), - migrations.AlterField( - model_name='supplierpart', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='URL for external supplier part link', null=True, verbose_name='Link'), - ), - migrations.AlterField( - model_name='supplierpart', - name='manufacturer', - field=models.ForeignKey(blank=True, help_text='Select manufacturer', limit_choices_to={'is_manufacturer': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='manufactured_parts', to='company.Company', verbose_name='Manufacturer'), - ), - migrations.AlterField( - model_name='supplierpart', - name='note', - field=models.CharField(blank=True, help_text='Notes', max_length=100, null=True, verbose_name='Note'), - ), - migrations.AlterField( - model_name='supplierpart', - name='packaging', - field=models.CharField(blank=True, help_text='Part packaging', max_length=50, null=True), - ), - migrations.AlterField( - model_name='supplierpart', - name='part', - field=models.ForeignKey(help_text='Select part', limit_choices_to={'purchaseable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.Part', verbose_name='Base Part'), - ), - migrations.AlterField( - model_name='supplierpart', - name='supplier', - field=models.ForeignKey(help_text='Select supplier', limit_choices_to={'is_supplier': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplied_parts', to='company.Company', verbose_name='Supplier'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0032_auto_20210403_1837.py b/src/backend/InvenTree/company/migrations/0032_auto_20210403_1837.py deleted file mode 100644 index 8b5f6fb89f..0000000000 --- a/src/backend/InvenTree/company/migrations/0032_auto_20210403_1837.py +++ /dev/null @@ -1,68 +0,0 @@ -# Generated by Django 3.0.7 on 2021-04-03 18:37 - -import InvenTree.fields -import company.models -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion -import stdimage.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0031_auto_20210103_2215'), - ] - - operations = [ - migrations.AlterField( - model_name='company', - name='image', - field=stdimage.models.StdImageField(blank=True, null=True, upload_to=company.models.rename_company_image, verbose_name='Image'), - ), - migrations.AlterField( - model_name='company', - name='is_customer', - field=models.BooleanField(default=False, help_text='Do you sell items to this company?', verbose_name='Is customer'), - ), - migrations.AlterField( - model_name='company', - name='is_manufacturer', - field=models.BooleanField(default=False, help_text='Does this company manufacture parts?', verbose_name='Is manufacturer'), - ), - migrations.AlterField( - model_name='company', - name='is_supplier', - field=models.BooleanField(default=True, help_text='Do you purchase items from this company?', verbose_name='Is supplier'), - ), - migrations.AlterField( - model_name='company', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external company information', verbose_name='Link'), - ), - migrations.AlterField( - model_name='company', - name='notes', - field=models.TextField(blank=True, verbose_name='Notes'), - ), - migrations.AlterField( - model_name='supplierpart', - name='base_cost', - field=models.DecimalField(decimal_places=3, default=0, help_text='Minimum charge (e.g. stocking fee)', max_digits=10, validators=[django.core.validators.MinValueValidator(0)], verbose_name='base cost'), - ), - migrations.AlterField( - model_name='supplierpart', - name='multiple', - field=models.PositiveIntegerField(default=1, help_text='Order multiple', validators=[django.core.validators.MinValueValidator(1)], verbose_name='multiple'), - ), - migrations.AlterField( - model_name='supplierpart', - name='packaging', - field=models.CharField(blank=True, help_text='Part packaging', max_length=50, null=True, verbose_name='Packaging'), - ), - migrations.AlterField( - model_name='supplierpricebreak', - name='part', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pricebreaks', to='company.SupplierPart', verbose_name='Part'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0033_auto_20210410_1528.py b/src/backend/InvenTree/company/migrations/0033_auto_20210410_1528.py deleted file mode 100644 index 12153a9ef6..0000000000 --- a/src/backend/InvenTree/company/migrations/0033_auto_20210410_1528.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.7 on 2021-04-10 05:28 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0032_auto_20210403_1837'), - ] - - operations = [ - migrations.AlterField( - model_name='company', - name='description', - field=models.CharField(blank=True, help_text='Description of the company', max_length=500, verbose_name='Company description'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0034_manufacturerpart.py b/src/backend/InvenTree/company/migrations/0034_manufacturerpart.py deleted file mode 100644 index f50919d59f..0000000000 --- a/src/backend/InvenTree/company/migrations/0034_manufacturerpart.py +++ /dev/null @@ -1,28 +0,0 @@ -import InvenTree.fields -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0033_auto_20210410_1528'), - ] - - operations = [ - migrations.CreateModel( - name='ManufacturerPart', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('MPN', models.CharField(help_text='Manufacturer Part Number', max_length=100, null=True, verbose_name='MPN')), - ('link', InvenTree.fields.InvenTreeURLField(blank=True, help_text='URL for external manufacturer part link', null=True, verbose_name='Link')), - ('description', models.CharField(blank=True, help_text='Manufacturer part description', max_length=250, null=True, verbose_name='Description')), - ('manufacturer', models.ForeignKey(help_text='Select manufacturer', limit_choices_to={'is_manufacturer': True}, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='manufactured_parts', to='company.Company', verbose_name='Manufacturer')), - ('part', models.ForeignKey(help_text='Select part', limit_choices_to={'purchaseable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='manufacturer_parts', to='part.Part', verbose_name='Base Part')), - ], - options={ - 'unique_together': {('part', 'manufacturer', 'MPN')}, - 'verbose_name': 'Manufacturer Part', - }, - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0035_supplierpart_update_1.py b/src/backend/InvenTree/company/migrations/0035_supplierpart_update_1.py deleted file mode 100644 index 657ae2464c..0000000000 --- a/src/backend/InvenTree/company/migrations/0035_supplierpart_update_1.py +++ /dev/null @@ -1,18 +0,0 @@ -import InvenTree.fields -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0034_manufacturerpart'), - ] - - operations = [ - migrations.AddField( - model_name='supplierpart', - name='manufacturer_part', - field=models.ForeignKey(blank=True, help_text='Select manufacturer part', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='company.ManufacturerPart', verbose_name='Manufacturer Part'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0036_supplierpart_update_2.py b/src/backend/InvenTree/company/migrations/0036_supplierpart_update_2.py deleted file mode 100644 index 9c986437e3..0000000000 --- a/src/backend/InvenTree/company/migrations/0036_supplierpart_update_2.py +++ /dev/null @@ -1,110 +0,0 @@ -import InvenTree.fields -from django.db import migrations, models, transaction -import django.db.models.deletion -from django.db.utils import IntegrityError - -def supplierpart_make_manufacturer_parts(apps, schema_editor): - Part = apps.get_model('part', 'Part') - ManufacturerPart = apps.get_model('company', 'ManufacturerPart') - SupplierPart = apps.get_model('company', 'SupplierPart') - - supplier_parts = SupplierPart.objects.all() - - if supplier_parts: - print(f'\nCreating ManufacturerPart Objects\n{"-"*10}') - for supplier_part in supplier_parts: - print(f'{supplier_part.supplier.name[:15].ljust(15)} | {supplier_part.SKU[:15].ljust(15)}\t', end='') - - if supplier_part.manufacturer_part: # pragma: no cover - print(f'[ERROR: MANUFACTURER PART ALREADY EXISTS]') - continue - - part = supplier_part.part - if not part: # pragma: no cover - print(f'[ERROR: SUPPLIER PART IS NOT CONNECTED TO PART]') - continue - - manufacturer = supplier_part.manufacturer - MPN = supplier_part.MPN - link = supplier_part.link - description = supplier_part.description - - if manufacturer or MPN: - print(f' | {part.name[:15].ljust(15)}', end='') - - try: - print(f' | {manufacturer.name[:15].ljust(15)}', end='') - except AttributeError: - print(f' | {"EMPTY MANUF".ljust(15)}', end='') - - try: - print(f' | {MPN[:15].ljust(15)}', end='') - except TypeError: - print(f' | {"EMPTY MPN".ljust(15)}', end='') - - print('\t', end='') - - # Create ManufacturerPart - manufacturer_part = ManufacturerPart(part=part, manufacturer=manufacturer, MPN=MPN, description=description, link=link) - created = False - try: - with transaction.atomic(): - manufacturer_part.save() - created = True - except IntegrityError: - manufacturer_part = ManufacturerPart.objects.get(part=part, manufacturer=manufacturer, MPN=MPN) - - # Link it to SupplierPart - supplier_part.manufacturer_part = manufacturer_part - supplier_part.save() - - if created: - print(f'[SUCCESS: MANUFACTURER PART CREATED]') - else: - print(f'[IGNORED: MANUFACTURER PART ALREADY EXISTS]') - else: - print(f'[IGNORED: MISSING MANUFACTURER DATA]') - - print(f'{"-"*10}\nDone\n') - -def supplierpart_populate_manufacturer_info(apps, schema_editor): # pragma: no cover - Part = apps.get_model('part', 'Part') - ManufacturerPart = apps.get_model('company', 'ManufacturerPart') - SupplierPart = apps.get_model('company', 'SupplierPart') - - supplier_parts = SupplierPart.objects.all() - - if supplier_parts: - print(f'\nSupplierPart: Populating Manufacturer Information\n{"-"*10}') - for supplier_part in supplier_parts: - print(f'{supplier_part.supplier.name[:15].ljust(15)} | {supplier_part.SKU[:15].ljust(15)}\t', end='') - - manufacturer_part = supplier_part.manufacturer_part - - if manufacturer_part: - if manufacturer_part.manufacturer: - supplier_part.manufacturer = manufacturer_part.manufacturer - - if manufacturer_part.MPN: - supplier_part.MPN = manufacturer_part.MPN - - supplier_part.save() - - print(f'[SUCCESS: UPDATED MANUFACTURER INFO]') - else: - print(f'[IGNORED: NO MANUFACTURER PART]') - - print(f'{"-"*10}\nDone\n') - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0035_supplierpart_update_1'), - ] - - operations = [ - # Make new ManufacturerPart with SupplierPart "manufacturer" and "MPN" - # fields, then link it to the new SupplierPart "manufacturer_part" field - migrations.RunPython(supplierpart_make_manufacturer_parts, reverse_code=supplierpart_populate_manufacturer_info), - ] diff --git a/src/backend/InvenTree/company/migrations/0037_supplierpart_update_3.py b/src/backend/InvenTree/company/migrations/0037_supplierpart_update_3.py deleted file mode 100644 index e3384be513..0000000000 --- a/src/backend/InvenTree/company/migrations/0037_supplierpart_update_3.py +++ /dev/null @@ -1,21 +0,0 @@ -import InvenTree.fields -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0036_supplierpart_update_2'), - ] - - operations = [ - migrations.RemoveField( - model_name='supplierpart', - name='MPN', - ), - migrations.RemoveField( - model_name='supplierpart', - name='manufacturer', - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0038_manufacturerpartparameter.py b/src/backend/InvenTree/company/migrations/0038_manufacturerpartparameter.py deleted file mode 100644 index dd833ccfa3..0000000000 --- a/src/backend/InvenTree/company/migrations/0038_manufacturerpartparameter.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 3.2.4 on 2021-06-20 07:48 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0037_supplierpart_update_3'), - ] - - operations = [ - migrations.CreateModel( - name='ManufacturerPartParameter', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(help_text='Parameter name', max_length=500, verbose_name='Name')), - ('value', models.CharField(help_text='Parameter value', max_length=500, verbose_name='Value')), - ('units', models.CharField(blank=True, help_text='Parameter units', max_length=64, null=True, verbose_name='Units')), - ('manufacturer_part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='parameters', to='company.manufacturerpart', verbose_name='Manufacturer Part')), - ], - options={ - 'verbose_name': 'Manufacturer Part Parameter', - 'unique_together': {('manufacturer_part', 'name')}, - }, - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0039_auto_20210701_0509.py b/src/backend/InvenTree/company/migrations/0039_auto_20210701_0509.py deleted file mode 100644 index 74dadcc340..0000000000 --- a/src/backend/InvenTree/company/migrations/0039_auto_20210701_0509.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 3.2.4 on 2021-07-01 05:09 - -import InvenTree.fields -from django.db import migrations -import djmoney.models.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0004_inventreesetting'), - ('company', '0038_manufacturerpartparameter'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpricebreak', - name='price', - field=InvenTree.fields.InvenTreeModelMoneyField(currency_choices=[], decimal_places=4, default_currency='', help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price'), - ), - migrations.AlterField( - model_name='supplierpricebreak', - name='price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0040_alter_company_currency.py b/src/backend/InvenTree/company/migrations/0040_alter_company_currency.py deleted file mode 100644 index 0415241521..0000000000 --- a/src/backend/InvenTree/company/migrations/0040_alter_company_currency.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.4 on 2021-07-02 13:21 - -import InvenTree.validators -import common.currency -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0039_auto_20210701_0509'), - ] - - operations = [ - migrations.AlterField( - model_name='company', - name='currency', - field=models.CharField(blank=True, default=common.currency.currency_code_default, help_text='Default currency used for this company', max_length=3, validators=[InvenTree.validators.validate_currency_code], verbose_name='Currency'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0041_alter_company_options.py b/src/backend/InvenTree/company/migrations/0041_alter_company_options.py deleted file mode 100644 index e6b1bed978..0000000000 --- a/src/backend/InvenTree/company/migrations/0041_alter_company_options.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.2.5 on 2021-10-04 20:41 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0040_alter_company_currency'), - ] - - operations = [ - migrations.AlterModelOptions( - name='company', - options={'ordering': ['name'], 'verbose_name': 'Company', 'verbose_name_plural': 'Companies'}, - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0042_supplierpricebreak_updated.py b/src/backend/InvenTree/company/migrations/0042_supplierpricebreak_updated.py deleted file mode 100644 index cf0788fe4e..0000000000 --- a/src/backend/InvenTree/company/migrations/0042_supplierpricebreak_updated.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.12 on 2022-03-14 22:19 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0041_alter_company_options'), - ] - - operations = [ - migrations.AddField( - model_name='supplierpricebreak', - name='updated', - field=models.DateTimeField(auto_now=True, null=True, verbose_name='last updated'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0043_manufacturerpartattachment.py b/src/backend/InvenTree/company/migrations/0043_manufacturerpartattachment.py deleted file mode 100644 index a0152385eb..0000000000 --- a/src/backend/InvenTree/company/migrations/0043_manufacturerpartattachment.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 3.2.13 on 2022-05-01 12:57 - -import InvenTree.fields -import InvenTree.models -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('company', '0042_supplierpricebreak_updated'), - ] - - operations = [ - migrations.CreateModel( - name='ManufacturerPartAttachment', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('attachment', models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to='attachments', verbose_name='Attachment')), - ('link', InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', null=True, verbose_name='Link')), - ('comment', models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment')), - ('upload_date', models.DateField(auto_now_add=True, null=True, verbose_name='upload date')), - ('manufacturer_part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='company.manufacturerpart', verbose_name='Manufacturer Part')), - ('user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User')), - ], - options={ - 'abstract': False, - }, - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0044_auto_20220607_2204.py b/src/backend/InvenTree/company/migrations/0044_auto_20220607_2204.py deleted file mode 100644 index 2a8f4890bf..0000000000 --- a/src/backend/InvenTree/company/migrations/0044_auto_20220607_2204.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-07 22:04 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0043_manufacturerpartattachment'), - ] - - operations = [ - migrations.AddField( - model_name='supplierpart', - name='availability_updated', - field=models.DateTimeField(blank=True, help_text='Date of last update of availability data', null=True, verbose_name='Availability Updated'), - ), - migrations.AddField( - model_name='supplierpart', - name='available', - field=models.DecimalField(decimal_places=3, default=0, help_text='Quantity available from supplier', max_digits=10, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Available'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0045_alter_company_notes.py b/src/backend/InvenTree/company/migrations/0045_alter_company_notes.py deleted file mode 100644 index 3aefa54afd..0000000000 --- a/src/backend/InvenTree/company/migrations/0045_alter_company_notes.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-20 11:23 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0044_auto_20220607_2204'), - ] - - operations = [ - migrations.AlterField( - model_name='company', - name='notes', - field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Company Notes', max_length=50000, null=True, verbose_name='Notes'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0046_alter_company_image.py b/src/backend/InvenTree/company/migrations/0046_alter_company_image.py deleted file mode 100644 index 518f6453d5..0000000000 --- a/src/backend/InvenTree/company/migrations/0046_alter_company_image.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-27 23:08 - -import company.models -from django.db import migrations -import stdimage.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0045_alter_company_notes'), - ] - - operations = [ - migrations.AlterField( - model_name='company', - name='image', - field=stdimage.models.StdImageField(blank=True, force_min_size=False, null=True, upload_to=company.models.rename_company_image, variations={'preview': (256, 256), 'thumbnail': (128, 128)}, verbose_name='Image'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0047_supplierpart_pack_size.py b/src/backend/InvenTree/company/migrations/0047_supplierpart_pack_size.py deleted file mode 100644 index 47a4e6b3fb..0000000000 --- a/src/backend/InvenTree/company/migrations/0047_supplierpart_pack_size.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.15 on 2022-09-05 04:21 - -import InvenTree.fields -import django.core.validators -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0046_alter_company_image'), - ] - - operations = [ - migrations.AddField( - model_name='supplierpart', - name='pack_size', - field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Unit quantity supplied in a single pack', max_digits=15, validators=[django.core.validators.MinValueValidator(0.001)], verbose_name='Pack Quantity'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0048_auto_20220913_0312.py b/src/backend/InvenTree/company/migrations/0048_auto_20220913_0312.py deleted file mode 100644 index 6f4aecc77d..0000000000 --- a/src/backend/InvenTree/company/migrations/0048_auto_20220913_0312.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.15 on 2022-09-13 03:12 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0047_supplierpart_pack_size'), - ] - - operations = [ - migrations.AddField( - model_name='supplierpart', - name='barcode_data', - field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), - ), - migrations.AddField( - model_name='supplierpart', - name='barcode_hash', - field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0048_auto_20220913_0312_squashed_0075_company_tax_id.py b/src/backend/InvenTree/company/migrations/0048_auto_20220913_0312_squashed_0075_company_tax_id.py new file mode 100644 index 0000000000..526bbe7c9c --- /dev/null +++ b/src/backend/InvenTree/company/migrations/0048_auto_20220913_0312_squashed_0075_company_tax_id.py @@ -0,0 +1,223 @@ +# Generated by Django 5.2.17 on 2026-09-09 00:28 + +import InvenTree.fields +import common.currency +import django.db.models.deletion +import djmoney.models.fields +import djmoney.models.validators +import taggit.managers +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + replaces = [('company', '0048_auto_20220913_0312'), ('company', '0049_company_metadata'), ('company', '0050_alter_company_website'), ('company', '0051_alter_supplierpricebreak_price'), ('company', '0052_alter_supplierpricebreak_updated'), ('company', '0053_supplierpart_updated'), ('company', '0054_companyattachment'), ('company', '0055_auto_20230317_0816'), ('company', '0056_alter_company_notes'), ('company', '0057_auto_20230427_2033'), ('company', '0058_auto_20230515_0004'), ('company', '0059_supplierpart_pack_units'), ('company', '0060_auto_20230519_0344'), ('company', '0061_remove_supplierpart_pack_size'), ('company', '0062_contact_metadata'), ('company', '0063_auto_20230502_1956'), ('company', '0064_move_address_field_to_address_model'), ('company', '0065_remove_company_address'), ('company', '0066_auto_20230616_2059'), ('company', '0067_alter_supplierpricebreak_price_currency'), ('company', '0068_auto_20231120_1108'), ('company', '0069_company_active'), ('company', '0070_remove_manufacturerpartattachment_manufacturer_part_and_more'), ('company', '0071_manufacturerpart_notes_supplierpart_notes'), ('company', '0072_auto_20250221_1236'), ('company', '0073_alter_address_link_alter_company_link_and_more'), ('company', '0074_alter_manufacturerpart_link'), ('company', '0075_company_tax_id')] + + dependencies = [ + ('common', '0004_inventreesetting'), + ('company', '0003_remove_supplierpart_minimum_squashed_0047_supplierpart_pack_size'), + ('part', '0111_auto_20230521_1350'), + ('stock', '0094_auto_20230220_0025'), + ('taggit', '0005_auto_20220424_2025'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='supplierpart', + name='barcode_data', + field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), + ), + migrations.AddField( + model_name='supplierpart', + name='barcode_hash', + field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), + ), + migrations.AddField( + model_name='company', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AlterField( + model_name='company', + name='website', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Company website URL', max_length=2000, verbose_name='Website'), + ), + migrations.AlterField( + model_name='supplierpricebreak', + name='price', + field=InvenTree.fields.InvenTreeModelMoneyField(currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Unit price at specified quantity', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price'), + ), + migrations.AlterField( + model_name='supplierpricebreak', + name='updated', + field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'), + ), + migrations.AddField( + model_name='supplierpart', + name='updated', + field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'), + ), + migrations.CreateModel( + name='CompanyAttachment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('attachment', models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to='attachments', verbose_name='Attachment')), + ('link', InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=2000, null=True, verbose_name='Link')), + ('comment', models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment')), + ('upload_date', models.DateField(auto_now_add=True, null=True, verbose_name='upload date')), + ('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='company.company', verbose_name='Company')), + ('user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='manufacturerpart', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AddField( + model_name='supplierpart', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AlterField( + model_name='company', + name='notes', + field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), + ), + migrations.AddField( + model_name='manufacturerpart', + name='tags', + field=taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), + ), + migrations.AddField( + model_name='supplierpart', + name='tags', + field=taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), + ), + migrations.AddField( + model_name='supplierpart', + name='pack_quantity', + field=models.CharField(blank=True, help_text='Total quantity supplied in a single pack. Leave empty for single items.', max_length=25, verbose_name='Pack Quantity'), + ), + migrations.AddField( + model_name='supplierpart', + name='pack_quantity_native', + field=InvenTree.fields.RoundingDecimalField(decimal_places=10, default=1, max_digits=20, null=True), + ), + migrations.RemoveField( + model_name='supplierpart', + name='pack_size', + ), + migrations.AddField( + model_name='contact', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.CreateModel( + name='Address', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(help_text='Title describing the address entry', max_length=100, verbose_name='Address title')), + ('primary', models.BooleanField(default=False, help_text='Set as primary address', verbose_name='Primary address')), + ('line1', models.CharField(blank=True, help_text='Address line 1', max_length=50, verbose_name='Line 1')), + ('line2', models.CharField(blank=True, help_text='Address line 2', max_length=50, verbose_name='Line 2')), + ('postal_code', models.CharField(blank=True, help_text='Postal code', max_length=10, verbose_name='Postal code')), + ('postal_city', models.CharField(blank=True, help_text='Postal code city', max_length=50, verbose_name='City')), + ('province', models.CharField(blank=True, help_text='State or province', max_length=50, verbose_name='State/Province')), + ('country', models.CharField(blank=True, help_text='Address country', max_length=50, verbose_name='Country')), + ('shipping_notes', models.CharField(blank=True, help_text='Notes for shipping courier', max_length=100, verbose_name='Courier shipping notes')), + ('internal_shipping_notes', models.CharField(blank=True, help_text='Shipping notes for internal use', max_length=100, verbose_name='Internal shipping notes')), + ('link', InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to address information (external)', max_length=2000, verbose_name='Link')), + ('company', models.ForeignKey(help_text='Select company', on_delete=django.db.models.deletion.CASCADE, related_name='addresses', to='company.company', verbose_name='Company')), + ], + ), + migrations.RemoveField( + model_name='company', + name='address', + ), + migrations.AlterModelOptions( + name='address', + options={'verbose_name': 'Address', 'verbose_name_plural': 'Addresses'}, + ), + migrations.AlterField( + model_name='address', + name='postal_city', + field=models.CharField(blank=True, help_text='Postal code city/region', max_length=50, verbose_name='City/Region'), + ), + migrations.AlterField( + model_name='supplierpricebreak', + name='price_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AddField( + model_name='manufacturerpart', + name='barcode_data', + field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), + ), + migrations.AddField( + model_name='manufacturerpart', + name='barcode_hash', + field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), + ), + migrations.AddField( + model_name='company', + name='active', + field=models.BooleanField(default=True, help_text='Is this company active?', verbose_name='Active'), + ), + migrations.AddField( + model_name='supplierpart', + name='active', + field=models.BooleanField(default=True, help_text='Is this supplier part active?', verbose_name='Active'), + ), + migrations.DeleteModel( + name='CompanyAttachment', + ), + migrations.DeleteModel( + name='ManufacturerPartAttachment', + ), + migrations.AddField( + model_name='manufacturerpart', + name='notes', + field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), + ), + migrations.AddField( + model_name='supplierpart', + name='notes', + field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), + ), + migrations.AlterField( + model_name='address', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to address information (external)', max_length=2000, verbose_name='Link'), + ), + migrations.AlterField( + model_name='company', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external company information', max_length=2000, verbose_name='Link'), + ), + migrations.AlterField( + model_name='company', + name='website', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Company website URL', max_length=2000, verbose_name='Website'), + ), + migrations.AlterField( + model_name='supplierpart', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='URL for external supplier part link', max_length=2000, null=True, verbose_name='Link'), + ), + migrations.AlterField( + model_name='manufacturerpart', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='URL for external manufacturer part link', max_length=2000, null=True, verbose_name='Link'), + ), + migrations.AddField( + model_name='company', + name='tax_id', + field=models.CharField(blank=True, help_text='Company Tax ID', max_length=50, verbose_name='Tax ID'), + ), + ] diff --git a/src/backend/InvenTree/company/migrations/0049_company_metadata.py b/src/backend/InvenTree/company/migrations/0049_company_metadata.py deleted file mode 100644 index de10f120cd..0000000000 --- a/src/backend/InvenTree/company/migrations/0049_company_metadata.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.16 on 2022-11-02 17:53 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0048_auto_20220913_0312'), - ] - - operations = [ - migrations.AddField( - model_name='company', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0050_alter_company_website.py b/src/backend/InvenTree/company/migrations/0050_alter_company_website.py deleted file mode 100644 index 575d5fd164..0000000000 --- a/src/backend/InvenTree/company/migrations/0050_alter_company_website.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.16 on 2022-11-10 01:08 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0049_company_metadata'), - ] - - operations = [ - migrations.AlterField( - model_name='company', - name='website', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Company website URL', verbose_name='Website'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0051_alter_supplierpricebreak_price.py b/src/backend/InvenTree/company/migrations/0051_alter_supplierpricebreak_price.py deleted file mode 100644 index 11f8d554be..0000000000 --- a/src/backend/InvenTree/company/migrations/0051_alter_supplierpricebreak_price.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.2.16 on 2022-11-11 01:50 - -import InvenTree.fields -from django.db import migrations -import djmoney.models.validators - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0004_inventreesetting'), - ('company', '0050_alter_company_website'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpricebreak', - name='price', - field=InvenTree.fields.InvenTreeModelMoneyField(currency_choices=[], decimal_places=6, default_currency='', help_text='Unit price at specified quantity', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0052_alter_supplierpricebreak_updated.py b/src/backend/InvenTree/company/migrations/0052_alter_supplierpricebreak_updated.py deleted file mode 100644 index a1689bf886..0000000000 --- a/src/backend/InvenTree/company/migrations/0052_alter_supplierpricebreak_updated.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.16 on 2023-01-15 14:04 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0051_alter_supplierpricebreak_price'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpricebreak', - name='updated', - field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0053_supplierpart_updated.py b/src/backend/InvenTree/company/migrations/0053_supplierpart_updated.py deleted file mode 100644 index 4c150893d0..0000000000 --- a/src/backend/InvenTree/company/migrations/0053_supplierpart_updated.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.16 on 2023-01-17 20:13 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0052_alter_supplierpricebreak_updated'), - ] - - operations = [ - migrations.AddField( - model_name='supplierpart', - name='updated', - field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0054_companyattachment.py b/src/backend/InvenTree/company/migrations/0054_companyattachment.py deleted file mode 100644 index 4996976ac1..0000000000 --- a/src/backend/InvenTree/company/migrations/0054_companyattachment.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 3.2.16 on 2023-02-15 12:55 - -import InvenTree.fields -import InvenTree.models -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('company', '0053_supplierpart_updated'), - ] - - operations = [ - migrations.CreateModel( - name='CompanyAttachment', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('attachment', models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to='attachments', verbose_name='Attachment')), - ('link', InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', null=True, verbose_name='Link')), - ('comment', models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment')), - ('upload_date', models.DateField(auto_now_add=True, null=True, verbose_name='upload date')), - ('company', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='company.company', verbose_name='Company')), - ('user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User')), - ], - options={ - 'abstract': False, - }, - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0055_auto_20230317_0816.py b/src/backend/InvenTree/company/migrations/0055_auto_20230317_0816.py deleted file mode 100644 index a4a85a7f35..0000000000 --- a/src/backend/InvenTree/company/migrations/0055_auto_20230317_0816.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-17 08:16 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0054_companyattachment'), - ] - - operations = [ - migrations.AddField( - model_name='manufacturerpart', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - migrations.AddField( - model_name='supplierpart', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0056_alter_company_notes.py b/src/backend/InvenTree/company/migrations/0056_alter_company_notes.py deleted file mode 100644 index 3ef0ea53b7..0000000000 --- a/src/backend/InvenTree/company/migrations/0056_alter_company_notes.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-19 00:37 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0055_auto_20230317_0816'), - ] - - operations = [ - migrations.AlterField( - model_name='company', - name='notes', - field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0057_auto_20230427_2033.py b/src/backend/InvenTree/company/migrations/0057_auto_20230427_2033.py deleted file mode 100644 index 874cc484d5..0000000000 --- a/src/backend/InvenTree/company/migrations/0057_auto_20230427_2033.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-27 20:33 - -from django.db import migrations -import taggit.managers - - -class Migration(migrations.Migration): - - dependencies = [ - ('taggit', '0005_auto_20220424_2025'), - ('company', '0056_alter_company_notes'), - ] - - operations = [ - migrations.AddField( - model_name='manufacturerpart', - name='tags', - field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), - ), - migrations.AddField( - model_name='supplierpart', - name='tags', - field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0058_auto_20230515_0004.py b/src/backend/InvenTree/company/migrations/0058_auto_20230515_0004.py deleted file mode 100644 index 8190df46fb..0000000000 --- a/src/backend/InvenTree/company/migrations/0058_auto_20230515_0004.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-15 00:04 - -from django.db import migrations -import taggit.managers - - -class Migration(migrations.Migration): - - dependencies = [ - ('taggit', '0005_auto_20220424_2025'), - ('company', '0057_auto_20230427_2033'), - ] - - operations = [ - migrations.AlterField( - model_name='manufacturerpart', - name='tags', - field=taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), - ), - migrations.AlterField( - model_name='supplierpart', - name='tags', - field=taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0059_supplierpart_pack_units.py b/src/backend/InvenTree/company/migrations/0059_supplierpart_pack_units.py deleted file mode 100644 index 8f97b4279d..0000000000 --- a/src/backend/InvenTree/company/migrations/0059_supplierpart_pack_units.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-19 03:41 - -from django.db import migrations, models - -import InvenTree.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0094_auto_20230220_0025'), - ('company', '0058_auto_20230515_0004'), - ] - - operations = [ - migrations.AddField( - model_name='supplierpart', - name='pack_quantity', - field=models.CharField(blank=True, help_text='Total quantity supplied in a single pack. Leave empty for single items.', max_length=25, verbose_name='Pack Quantity'), - ), - migrations.AddField( - model_name='supplierpart', - name='pack_quantity_native', - field=InvenTree.fields.RoundingDecimalField(decimal_places=10, default=1, max_digits=20, null=True), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0060_auto_20230519_0344.py b/src/backend/InvenTree/company/migrations/0060_auto_20230519_0344.py deleted file mode 100644 index 34e6d2fc69..0000000000 --- a/src/backend/InvenTree/company/migrations/0060_auto_20230519_0344.py +++ /dev/null @@ -1,51 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-19 03:44 - -from django.db import migrations - -from InvenTree.helpers import normalize - - -def update_supplier_part_units(apps, schema_editor): - """Migrate existing supplier part units to new field""" - - SupplierPart = apps.get_model('company', 'SupplierPart') - - supplier_parts = SupplierPart.objects.all() - - for sp in supplier_parts: - pack_size = normalize(sp.pack_size) - sp.pack_quantity = str(pack_size) - sp.pack_quantity_native = pack_size - sp.save() - - if supplier_parts.count() > 0: - print(f"Updated {supplier_parts.count()} supplier part units") - - -def reverse_pack_quantity(apps, schema_editor): - """Reverse the migrations""" - - SupplierPart = apps.get_model('company', 'SupplierPart') - - supplier_parts = SupplierPart.objects.all() - - for sp in supplier_parts: - sp.pack_size = sp.pack_quantity_native - sp.save() - - if supplier_parts.count() > 0: - print(f"Updated {supplier_parts.count()} supplier part units") - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0059_supplierpart_pack_units'), - ('part', '0111_auto_20230521_1350'), - ] - - operations = [ - migrations.RunPython( - code=update_supplier_part_units, - reverse_code=reverse_pack_quantity, - ) - ] diff --git a/src/backend/InvenTree/company/migrations/0061_remove_supplierpart_pack_size.py b/src/backend/InvenTree/company/migrations/0061_remove_supplierpart_pack_size.py deleted file mode 100644 index 0ae404be05..0000000000 --- a/src/backend/InvenTree/company/migrations/0061_remove_supplierpart_pack_size.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-19 04:03 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0060_auto_20230519_0344'), - ] - - operations = [ - migrations.RemoveField( - model_name='supplierpart', - name='pack_size', - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0062_contact_metadata.py b/src/backend/InvenTree/company/migrations/0062_contact_metadata.py deleted file mode 100644 index eb3f638076..0000000000 --- a/src/backend/InvenTree/company/migrations/0062_contact_metadata.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-25 16:06 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0061_remove_supplierpart_pack_size'), - ] - - operations = [ - migrations.AddField( - model_name='contact', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0063_auto_20230502_1956.py b/src/backend/InvenTree/company/migrations/0063_auto_20230502_1956.py deleted file mode 100644 index c0cce4e188..0000000000 --- a/src/backend/InvenTree/company/migrations/0063_auto_20230502_1956.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 3.2.18 on 2023-05-02 19:56 - -import InvenTree.fields -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0062_contact_metadata'), - ] - - operations = [ - migrations.CreateModel( - name='Address', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(help_text='Title describing the address entry', max_length=100, verbose_name='Address title')), - ('primary', models.BooleanField(default=False, help_text='Set as primary address', verbose_name='Primary address')), - ('line1', models.CharField(blank=True, help_text='Address line 1', max_length=50, verbose_name='Line 1')), - ('line2', models.CharField(blank=True, help_text='Address line 2', max_length=50, verbose_name='Line 2')), - ('postal_code', models.CharField(blank=True, help_text='Postal code', max_length=10, verbose_name='Postal code')), - ('postal_city', models.CharField(blank=True, help_text='Postal code city', max_length=50, verbose_name='City')), - ('province', models.CharField(blank=True, help_text='State or province', max_length=50, verbose_name='State/Province')), - ('country', models.CharField(blank=True, help_text='Address country', max_length=50, verbose_name='Country')), - ('shipping_notes', models.CharField(blank=True, help_text='Notes for shipping courier', max_length=100, verbose_name='Courier shipping notes')), - ('internal_shipping_notes', models.CharField(blank=True, help_text='Shipping notes for internal use', max_length=100, verbose_name='Internal shipping notes')), - ('link', InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to address information (external)', verbose_name='Link')), - ('company', models.ForeignKey(help_text='Select company', on_delete=django.db.models.deletion.CASCADE, related_name='addresses', to='company.company', verbose_name='Company')), - ], - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0064_move_address_field_to_address_model.py b/src/backend/InvenTree/company/migrations/0064_move_address_field_to_address_model.py deleted file mode 100644 index 76d7d3b478..0000000000 --- a/src/backend/InvenTree/company/migrations/0064_move_address_field_to_address_model.py +++ /dev/null @@ -1,37 +0,0 @@ -# Generated by Django 3.2.18 on 2023-05-02 20:41 - -from django.db import migrations - -def move_address_to_new_model(apps, schema_editor): - Company = apps.get_model('company', 'Company') - Address = apps.get_model('company', 'Address') - for company in Company.objects.all(): - if company.address != '': - # Address field might exceed length of new model fields - l1 = company.address[:50] - l2 = company.address[50:100] - Address.objects.create(company=company, - title="Primary", - primary=True, - line1=l1, - line2=l2) - company.address = '' - company.save() - -def revert_address_move(apps, schema_editor): - Address = apps.get_model('company', 'Address') - for address in Address.objects.all(): - address.company.address = f'{address.line1}{address.line2}' - address.company.save() - address.delete() - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0063_auto_20230502_1956'), - ] - - operations = [ - migrations.RunPython(move_address_to_new_model, reverse_code=revert_address_move) - ] diff --git a/src/backend/InvenTree/company/migrations/0065_remove_company_address.py b/src/backend/InvenTree/company/migrations/0065_remove_company_address.py deleted file mode 100644 index 4beaeca792..0000000000 --- a/src/backend/InvenTree/company/migrations/0065_remove_company_address.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.2.18 on 2023-05-13 14:53 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0064_move_address_field_to_address_model'), - ] - - operations = [ - migrations.RemoveField( - model_name='company', - name='address', - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0066_auto_20230616_2059.py b/src/backend/InvenTree/company/migrations/0066_auto_20230616_2059.py deleted file mode 100644 index f5160f3255..0000000000 --- a/src/backend/InvenTree/company/migrations/0066_auto_20230616_2059.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 3.2.19 on 2023-06-16 20:59 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0065_remove_company_address'), - ] - - operations = [ - migrations.AlterModelOptions( - name='address', - options={ - 'verbose_name': 'Address', - 'verbose_name_plural': 'Addresses' - }, - ), - migrations.AlterField( - model_name='address', - name='postal_city', - field=models.CharField(blank=True, help_text='Postal code city/region', max_length=50, verbose_name='City/Region'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0067_alter_supplierpricebreak_price_currency.py b/src/backend/InvenTree/company/migrations/0067_alter_supplierpricebreak_price_currency.py deleted file mode 100644 index 4c18def143..0000000000 --- a/src/backend/InvenTree/company/migrations/0067_alter_supplierpricebreak_price_currency.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.22 on 2023-10-24 16:44 - -from django.db import migrations -import djmoney.models.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0066_auto_20230616_2059'), - ] - - operations = [ - migrations.AlterField( - model_name='supplierpricebreak', - name='price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0068_auto_20231120_1108.py b/src/backend/InvenTree/company/migrations/0068_auto_20231120_1108.py deleted file mode 100644 index 6a4cba6e29..0000000000 --- a/src/backend/InvenTree/company/migrations/0068_auto_20231120_1108.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.23 on 2023-11-20 11:08 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0067_alter_supplierpricebreak_price_currency'), - ] - - operations = [ - migrations.AddField( - model_name='manufacturerpart', - name='barcode_data', - field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), - ), - migrations.AddField( - model_name='manufacturerpart', - name='barcode_hash', - field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0069_company_active.py b/src/backend/InvenTree/company/migrations/0069_company_active.py deleted file mode 100644 index 120046f6fc..0000000000 --- a/src/backend/InvenTree/company/migrations/0069_company_active.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 4.2.11 on 2024-04-15 14:42 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0068_auto_20231120_1108'), - ] - - operations = [ - migrations.AddField( - model_name='company', - name='active', - field=models.BooleanField(default=True, help_text='Is this company active?', verbose_name='Active'), - ), - migrations.AddField( - model_name='supplierpart', - name='active', - field=models.BooleanField(default=True, help_text='Is this supplier part active?', verbose_name='Active'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0070_remove_manufacturerpartattachment_manufacturer_part_and_more.py b/src/backend/InvenTree/company/migrations/0070_remove_manufacturerpartattachment_manufacturer_part_and_more.py deleted file mode 100644 index d0bec93f7d..0000000000 --- a/src/backend/InvenTree/company/migrations/0070_remove_manufacturerpartattachment_manufacturer_part_and_more.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 4.2.12 on 2024-06-09 09:02 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0050_auto_20240508_0138'), - ('common', '0026_auto_20240608_1238'), - ('company', '0069_company_active'), - ('order', '0099_alter_salesorder_status'), - ('part', '0123_parttesttemplate_choices'), - ('stock', '0110_alter_stockitemtestresult_finished_datetime_and_more') - ] - - operations = [ - migrations.DeleteModel( - name='CompanyAttachment', - ), - migrations.DeleteModel( - name='ManufacturerPartAttachment', - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0071_manufacturerpart_notes_supplierpart_notes.py b/src/backend/InvenTree/company/migrations/0071_manufacturerpart_notes_supplierpart_notes.py deleted file mode 100644 index b7d6c8caf7..0000000000 --- a/src/backend/InvenTree/company/migrations/0071_manufacturerpart_notes_supplierpart_notes.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 4.2.11 on 2024-07-16 12:58 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0070_remove_manufacturerpartattachment_manufacturer_part_and_more'), - ] - - operations = [ - migrations.AddField( - model_name='manufacturerpart', - name='notes', - field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), - ), - migrations.AddField( - model_name='supplierpart', - name='notes', - field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0072_auto_20250221_1236.py b/src/backend/InvenTree/company/migrations/0072_auto_20250221_1236.py deleted file mode 100644 index 4e111dd434..0000000000 --- a/src/backend/InvenTree/company/migrations/0072_auto_20250221_1236.py +++ /dev/null @@ -1,39 +0,0 @@ -# Generated by Django 4.2.19 on 2025-02-21 12:36 - -import InvenTree.fields -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("company", "0071_manufacturerpart_notes_supplierpart_notes"), - ] - - operations = [ - migrations.AlterField( - model_name='company', - name='website', - field=models.TextField(null=True, blank=True) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - migrations.AlterField( - model_name='company', - name='link', - field=models.TextField(null=True, blank=True) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - migrations.AlterField( - model_name='address', - name='link', - field=models.TextField(null=True, blank=True) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - migrations.AlterField( - model_name='supplierpart', - name='link', - field=models.TextField(null=True, blank=True) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - migrations.AlterField( - model_name='manufacturerpart', - name='link', - field=models.TextField(null=True, blank=True) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0073_alter_address_link_alter_company_link_and_more.py b/src/backend/InvenTree/company/migrations/0073_alter_address_link_alter_company_link_and_more.py deleted file mode 100644 index 06657c796c..0000000000 --- a/src/backend/InvenTree/company/migrations/0073_alter_address_link_alter_company_link_and_more.py +++ /dev/null @@ -1,55 +0,0 @@ -# Generated by Django 4.2.19 on 2025-02-21 13:46 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("company", "0072_auto_20250221_1236"), - ] - - operations = [ - migrations.AlterField( - model_name="address", - name="link", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="Link to address information (external)", - max_length=2000, - verbose_name="Link", - ), - ), - migrations.AlterField( - model_name="company", - name="link", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="Link to external company information", - max_length=2000, - verbose_name="Link", - ), - ), - migrations.AlterField( - model_name="company", - name="website", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="Company website URL", - max_length=2000, - verbose_name="Website", - ), - ), - migrations.AlterField( - model_name="supplierpart", - name="link", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="URL for external supplier part link", - max_length=2000, - null=True, - verbose_name="Link", - ), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0074_alter_manufacturerpart_link.py b/src/backend/InvenTree/company/migrations/0074_alter_manufacturerpart_link.py deleted file mode 100644 index 80261f1156..0000000000 --- a/src/backend/InvenTree/company/migrations/0074_alter_manufacturerpart_link.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 4.2.19 on 2025-02-21 14:51 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("company", "0073_alter_address_link_alter_company_link_and_more"), - ] - - operations = [ - migrations.AlterField( - model_name="manufacturerpart", - name="link", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="URL for external manufacturer part link", - max_length=2000, - null=True, - verbose_name="Link", - ), - ), - ] diff --git a/src/backend/InvenTree/company/migrations/0075_company_tax_id.py b/src/backend/InvenTree/company/migrations/0075_company_tax_id.py deleted file mode 100644 index 15f8f2ff46..0000000000 --- a/src/backend/InvenTree/company/migrations/0075_company_tax_id.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 4.2.21 on 2025-05-17 17:16 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("company", "0074_alter_manufacturerpart_link"), - ] - - operations = [ - migrations.AddField( - model_name="company", - name="tax_id", - field=models.CharField( - blank=True, - help_text="Company Tax ID", - max_length=50, - verbose_name="Tax ID", - ), - ), - ] diff --git a/src/backend/InvenTree/company/test_migrations.py b/src/backend/InvenTree/company/test_migrations.py index 741c7f187f..4ae1a64622 100644 --- a/src/backend/InvenTree/company/test_migrations.py +++ b/src/backend/InvenTree/company/test_migrations.py @@ -26,343 +26,6 @@ class TestForwardMigrations(MigratorTestCase): self.assertEqual(Company.objects.count(), 1) -class TestManufacturerField(MigratorTestCase): - """Tests for migration 0019 which migrates from old 'manufacturer_name' field to new 'manufacturer' field.""" - - migrate_from = ('company', '0018_supplierpart_manufacturer') - migrate_to = ('company', '0019_auto_20200413_0642') - - def prepare(self): - """Prepare the database by adding some test data 'before' the change. - - Changes: - - Part object - - Company object (supplier) - - SupplierPart object - """ - Part = self.old_state.apps.get_model('part', 'part') - Company = self.old_state.apps.get_model('company', 'company') - SupplierPart = self.old_state.apps.get_model('company', 'supplierpart') - - # Create an initial part - part = Part.objects.create(name='Screw', description='A single screw') - - # Create a company to act as the supplier - supplier = Company.objects.create( - name='Supplier', - description='A supplier of parts', - is_supplier=True, - is_customer=False, - ) - - # Add some SupplierPart objects - SupplierPart.objects.create( - part=part, supplier=supplier, SKU='SCREW.001', manufacturer_name='ACME' - ) - - SupplierPart.objects.create( - part=part, supplier=supplier, SKU='SCREW.002', manufacturer_name='Zero Corp' - ) - - self.assertEqual(Company.objects.count(), 1) - - def test_company_objects(self): - """Test that the new companies have been created successfully.""" - # Two additional company objects should have been created - Company = self.new_state.apps.get_model('company', 'company') - self.assertEqual(Company.objects.count(), 3) - - # The new company/ies must be marked as "manufacturers" - acme = Company.objects.get(name='ACME') - self.assertTrue(acme.is_manufacturer) - - SupplierPart = self.new_state.apps.get_model('company', 'supplierpart') - parts = SupplierPart.objects.filter(manufacturer=acme) - self.assertEqual(parts.count(), 1) - part = parts.first() - - # Checks on the SupplierPart object - self.assertEqual(part.manufacturer_name, 'ACME') - self.assertEqual(part.manufacturer.name, 'ACME') - - -class TestManufacturerPart(MigratorTestCase): - """Tests for migration 0034-0037 which added and transitioned to the ManufacturerPart model.""" - - migrate_from = ('company', '0033_auto_20210410_1528') - migrate_to = ('company', '0037_supplierpart_update_3') - - def prepare(self): - """Prepare the database by adding some test data 'before' the change. - - Changes: - - Part object - - Company object (supplier) - - SupplierPart object - """ - Part = self.old_state.apps.get_model('part', 'part') - Company = self.old_state.apps.get_model('company', 'company') - SupplierPart = self.old_state.apps.get_model('company', 'supplierpart') - - # Create an initial part - part = Part.objects.create( - name='CAP CER 0.1UF 10V X5R 0402', - description='CAP CER 0.1UF 10V X5R 0402', - purchaseable=True, - level=0, - tree_id=0, - lft=0, - rght=0, - ) - - # Create a manufacturer - manufacturer = Company.objects.create( - name='Murata', - description='Makes capacitors', - is_manufacturer=True, - is_supplier=False, - is_customer=False, - ) - - # Create suppliers - supplier_1 = Company.objects.create( - name='Digi-Key', - description='A supplier of components', - is_manufacturer=False, - is_supplier=True, - is_customer=False, - ) - - supplier_2 = Company.objects.create( - name='Mouser', - description='We sell components', - is_manufacturer=False, - is_supplier=True, - is_customer=False, - ) - - # Add some SupplierPart objects - SupplierPart.objects.create( - part=part, - supplier=supplier_1, - SKU='DK-MUR-CAP-123456-ND', - manufacturer=manufacturer, - MPN='MUR-CAP-123456', - ) - - SupplierPart.objects.create( - part=part, - supplier=supplier_1, - SKU='DK-MUR-CAP-987654-ND', - manufacturer=manufacturer, - MPN='MUR-CAP-987654', - ) - - SupplierPart.objects.create( - part=part, - supplier=supplier_2, - SKU='CAP-CER-01UF', - manufacturer=manufacturer, - MPN='MUR-CAP-123456', - ) - - # No MPN - SupplierPart.objects.create( - part=part, - supplier=supplier_2, - SKU='CAP-CER-01UF-1', - manufacturer=manufacturer, - ) - - # No Manufacturer - SupplierPart.objects.create( - part=part, supplier=supplier_2, SKU='CAP-CER-01UF-2', MPN='MUR-CAP-123456' - ) - - # No Manufacturer data - SupplierPart.objects.create( - part=part, supplier=supplier_2, SKU='CAP-CER-01UF-3' - ) - - def test_manufacturer_part_objects(self): - """Test that the new companies have been created successfully.""" - # Check on the SupplierPart objects - SupplierPart = self.new_state.apps.get_model('company', 'supplierpart') - - supplier_parts = SupplierPart.objects.all() - self.assertEqual(supplier_parts.count(), 6) - - supplier_parts = SupplierPart.objects.filter(supplier__name='Mouser') - self.assertEqual(supplier_parts.count(), 4) - - # Check on the ManufacturerPart objects - ManufacturerPart = self.new_state.apps.get_model('company', 'manufacturerpart') - - manufacturer_parts = ManufacturerPart.objects.all() - self.assertEqual(manufacturer_parts.count(), 4) - - manufacturer_part = manufacturer_parts.first() - self.assertEqual(manufacturer_part.MPN, 'MUR-CAP-123456') - - -class TestCurrencyMigration(MigratorTestCase): - """Tests for upgrade from basic currency support to django-money.""" - - migrate_from = ('company', '0025_auto_20201110_1001') - migrate_to = ('company', '0026_auto_20201110_1011') - - def prepare(self): - """Prepare some data. - - Changes: - - A part to buy - - A supplier to buy from - - A supplier part - - Multiple currency objects - - Multiple supplier price breaks - """ - Part = self.old_state.apps.get_model('part', 'part') - - part = Part.objects.create( - name='PART', - description='A purchaseable part', - purchaseable=True, - level=0, - tree_id=0, - lft=0, - rght=0, - ) - - Company = self.old_state.apps.get_model('company', 'company') - - supplier = Company.objects.create( - name='Supplier', description='A supplier', is_supplier=True - ) - - SupplierPart = self.old_state.apps.get_model('company', 'supplierpart') - - sp = SupplierPart.objects.create(part=part, supplier=supplier, SKU='12345') - - Currency = self.old_state.apps.get_model('common', 'currency') - - aud = Currency.objects.create( - symbol='$', suffix='AUD', description='Australian Dollars', value=1.0 - ) - usd = Currency.objects.create( - symbol='$', suffix='USD', description='US Dollars', value=1.0 - ) - - PB = self.old_state.apps.get_model('company', 'supplierpricebreak') - - PB.objects.create(part=sp, quantity=10, cost=5, currency=aud) - PB.objects.create(part=sp, quantity=20, cost=3, currency=aud) - PB.objects.create(part=sp, quantity=30, cost=2, currency=aud) - - PB.objects.create(part=sp, quantity=40, cost=2, currency=usd) - PB.objects.create(part=sp, quantity=50, cost=2, currency=usd) - - for pb in PB.objects.all(): - self.assertIsNone(pb.price) - - def test_currency_migration(self): - """Test database state after applying migrations.""" - PB = self.new_state.apps.get_model('company', 'supplierpricebreak') - - for pb in PB.objects.all(): - # Test that a price has been assigned - self.assertIsNotNone(pb.price) - - -class TestAddressMigration(MigratorTestCase): - """Test moving address data into Address model.""" - - migrate_from = ('company', '0063_auto_20230502_1956') - migrate_to = ('company', '0064_move_address_field_to_address_model') - - # Setting up string values for reuse - short_l1 = 'Less than 50 characters long address' - long_l1 = 'More than 50 characters long address testing line ' - l2 = 'splitting functionality' - - def prepare(self): - """Set up some companies with addresses.""" - Company = self.old_state.apps.get_model('company', 'company') - - Company.objects.create(name='Company 1', address=self.short_l1) - Company.objects.create(name='Company 2', address=self.long_l1 + self.l2) - - def test_address_migration(self): - """Test database state after applying the migration.""" - Address = self.new_state.apps.get_model('company', 'address') - Company = self.new_state.apps.get_model('company', 'company') - - c1 = Company.objects.filter(name='Company 1').first() - c2 = Company.objects.filter(name='Company 2').first() - - self.assertEqual(Address.objects.count(), 2) - - a1 = Address.objects.filter(company=c1.pk).first() - a2 = Address.objects.filter(company=c2.pk).first() - - self.assertEqual(a1.line1, self.short_l1) - self.assertEqual(a1.line2, '') - self.assertEqual(a2.line1, self.long_l1) - self.assertEqual(a2.line2, self.l2) - self.assertEqual(c1.address, '') - self.assertEqual(c2.address, '') - - -class TestSupplierPartQuantity(MigratorTestCase): - """Test that the supplier part quantity is correctly migrated.""" - - migrate_from = ('company', '0058_auto_20230515_0004') - migrate_to = ('company', '0062_contact_metadata') - - def prepare(self): - """Prepare a number of SupplierPart objects.""" - Part = self.old_state.apps.get_model('part', 'part') - Company = self.old_state.apps.get_model('company', 'company') - SupplierPart = self.old_state.apps.get_model('company', 'supplierpart') - - self.part = Part.objects.create( - name='PART', - description='A purchaseable part', - purchaseable=True, - level=0, - tree_id=0, - lft=0, - rght=0, - ) - - self.supplier = Company.objects.create( - name='Supplier', description='A supplier', is_supplier=True - ) - - self.supplier_parts = [] - - for i in range(10): - self.supplier_parts.append( - SupplierPart.objects.create( - part=self.part, - supplier=self.supplier, - SKU=f'SKU-{i}', - pack_size=i + 1, - ) - ) - - def test_supplier_part_quantity(self): - """Test that the supplier part quantity is correctly migrated.""" - SupplierPart = self.new_state.apps.get_model('company', 'supplierpart') - - for i, sp in enumerate(SupplierPart.objects.all()): - self.assertEqual(sp.pack_quantity, str(i + 1)) - self.assertEqual(sp.pack_quantity_native, i + 1) - - # And the 'pack_size' attribute has been removed - with self.assertRaises(AttributeError): - sp.pack_size - - class TestManufacturerPartParameterMigration(MigratorTestCase): """Test migration of ManufacturerPartParameter data. @@ -375,7 +38,7 @@ class TestManufacturerPartParameterMigration(MigratorTestCase): - ManufacturerPartParameter model was removed """ - migrate_from = ('common', '0038_alter_attachment_model_type') + migrate_from = ('company', '0076_alter_company_image') migrate_to = ('company', '0077_delete_manufacturerpartparameter') def prepare(self): diff --git a/src/backend/InvenTree/order/migrations/0001_initial.py b/src/backend/InvenTree/order/migrations/0001_initial.py deleted file mode 100644 index 498aad2983..0000000000 --- a/src/backend/InvenTree/order/migrations/0001_initial.py +++ /dev/null @@ -1,50 +0,0 @@ -# Generated by Django 2.2 on 2019-06-04 12:17 - -from django.conf import settings -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('company', '0005_auto_20190525_2356'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='PurchaseOrder', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('reference', models.CharField(help_text='Order reference', max_length=64, unique=True)), - ('description', models.CharField(help_text='Order description', max_length=250)), - ('creation_date', models.DateField(auto_now=True)), - ('issue_date', models.DateField(blank=True, null=True)), - ('notes', models.TextField(blank=True, help_text='Order notes')), - ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), - ('supplier', models.ForeignKey(help_text='Company', on_delete=django.db.models.deletion.CASCADE, related_name='Orders', to='company.Company')), - ], - options={ - 'abstract': False, - 'verbose_name': 'Purchase Order' - }, - ), - migrations.CreateModel( - name='PurchaseOrderLineItem', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('quantity', models.PositiveIntegerField(default=1, help_text='Item quantity', validators=[django.core.validators.MinValueValidator(0)])), - ('reference', models.CharField(blank=True, help_text='Line item reference', max_length=100)), - ('received', models.PositiveIntegerField(default=0, help_text='Number of items received')), - ('order', models.ForeignKey(help_text='Purchase Order', on_delete=django.db.models.deletion.CASCADE, related_name='lines', to='order.PurchaseOrder')), - ], - options={ - 'abstract': False, - 'verbose_name': 'Purchase Order Line Item' - }, - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0001_squashed_0023_auto_20200420_2309.py b/src/backend/InvenTree/order/migrations/0001_squashed_0023_auto_20200420_2309.py new file mode 100644 index 0000000000..b8c39395e6 --- /dev/null +++ b/src/backend/InvenTree/order/migrations/0001_squashed_0023_auto_20200420_2309.py @@ -0,0 +1,411 @@ +# Generated by Django 5.2.17 on 2026-09-09 06:48 + +import InvenTree.fields +import django.core.validators +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + replaces = [ + ("order", "0001_initial"), + ("order", "0002_auto_20190604_2224"), + ("order", "0003_auto_20190604_2226"), + ("order", "0004_purchaseorder_status"), + ("order", "0005_purchaseorderlineitem_part"), + ("order", "0006_auto_20190605_2056"), + ("order", "0007_auto_20190605_2138"), + ("order", "0008_auto_20190605_2140"), + ("order", "0009_auto_20190606_2133"), + ("order", "0010_purchaseorderlineitem_notes"), + ("order", "0011_auto_20190615_1928"), + ("order", "0012_auto_20190617_1943"), + ("order", "0013_auto_20191118_2323"), + ("order", "0014_auto_20191118_2328"), + ("order", "0015_auto_20200201_2346"), + ("order", "0016_purchaseorderattachment"), + ("order", "0017_auto_20200331_1000"), + ("order", "0018_auto_20200406_0151"), + ("order", "0019_purchaseorder_supplier_reference"), + ("order", "0020_auto_20200420_0940"), + ("order", "0021_auto_20200420_1010"), + ("order", "0022_salesorderlineitem_part"), + ("order", "0023_auto_20200420_2309"), + ] + + initial = True + + dependencies = [ + ("company", "0005_auto_20190525_2356"), + ("company", "0021_remove_supplierpart_manufacturer_name"), + ("part", "0035_auto_20200406_0045"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="PurchaseOrder", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "reference", + models.CharField( + help_text="Order reference", max_length=64, unique=True + ), + ), + ( + "description", + models.CharField(help_text="Order description", max_length=250), + ), + ("creation_date", models.DateField(blank=True, null=True)), + ("issue_date", models.DateField(blank=True, null=True)), + ("notes", models.TextField(blank=True, help_text="Order notes")), + ( + "created_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "supplier", + models.ForeignKey( + help_text="Supplier", + limit_choices_to={"is_supplier": True}, + on_delete=django.db.models.deletion.CASCADE, + related_name="purchase_orders", + to="company.company", + ), + ), + ( + "link", + models.URLField(blank=True, help_text="Link to external page"), + ), + ( + "status", + models.PositiveIntegerField( + choices=[ + (10, "Pending"), + (20, "Placed"), + (30, "Complete"), + (40, "Cancelled"), + (50, "Lost"), + (60, "Returned"), + ], + default=10, + help_text="Order status", + ), + ), + ("complete_date", models.DateField(blank=True, null=True)), + ( + "received_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "supplier_reference", + models.CharField( + blank=True, + help_text="Supplier order reference code", + max_length=64, + ), + ), + ], + options={ + "abstract": False, + "verbose_name": "Purchase Order", + }, + ), + migrations.CreateModel( + name="PurchaseOrderAttachment", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "attachment", + models.FileField( + help_text="Select file to attach", upload_to="attachments" + ), + ), + ("comment", models.CharField(help_text="File comment", max_length=100)), + ( + "order", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="attachments", + to="order.purchaseorder", + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="PurchaseOrderLineItem", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "quantity", + InvenTree.fields.RoundingDecimalField( + decimal_places=5, + default=1, + help_text="Item quantity", + max_digits=15, + validators=[django.core.validators.MinValueValidator(0)], + ), + ), + ( + "reference", + models.CharField( + blank=True, help_text="Line item reference", max_length=100 + ), + ), + ( + "received", + models.DecimalField( + decimal_places=5, + default=0, + help_text="Number of items received", + max_digits=15, + ), + ), + ( + "order", + models.ForeignKey( + help_text="Purchase Order", + on_delete=django.db.models.deletion.CASCADE, + related_name="lines", + to="order.purchaseorder", + ), + ), + ( + "part", + models.ForeignKey( + blank=True, + help_text="Supplier part", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="purchase_order_line_items", + to="company.supplierpart", + ), + ), + ( + "notes", + models.CharField( + blank=True, help_text="Line item notes", max_length=500 + ), + ), + ], + options={ + "abstract": False, + "verbose_name": "Purchase Order Line Item", + "unique_together": {("order", "part")}, + }, + ), + migrations.CreateModel( + name="SalesOrder", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "reference", + models.CharField( + help_text="Order reference", max_length=64, unique=True + ), + ), + ( + "description", + models.CharField(help_text="Order description", max_length=250), + ), + ( + "link", + models.URLField(blank=True, help_text="Link to external page"), + ), + ("creation_date", models.DateField(blank=True, null=True)), + ( + "status", + models.PositiveIntegerField( + choices=[ + (10, "Pending"), + (20, "Placed"), + (30, "Complete"), + (40, "Cancelled"), + (50, "Lost"), + (60, "Returned"), + ], + default=10, + help_text="Order status", + ), + ), + ("issue_date", models.DateField(blank=True, null=True)), + ("complete_date", models.DateField(blank=True, null=True)), + ("notes", models.TextField(blank=True, help_text="Order notes")), + ( + "customer_reference", + models.CharField( + blank=True, + help_text="Customer order reference code", + max_length=64, + ), + ), + ( + "created_by", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + ( + "customer", + models.ForeignKey( + help_text="Customer", + limit_choices_to={"is_customer": True}, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="sales_orders", + to="company.company", + ), + ), + ], + options={ + "abstract": False, + "verbose_name": "Sales Order", + }, + ), + migrations.CreateModel( + name="SalesOrderAttachment", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "attachment", + models.FileField( + help_text="Select file to attach", upload_to="attachments" + ), + ), + ("comment", models.CharField(help_text="File comment", max_length=100)), + ( + "order", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="attachments", + to="order.salesorder", + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="SalesOrderLineItem", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "quantity", + InvenTree.fields.RoundingDecimalField( + decimal_places=5, + default=1, + help_text="Item quantity", + max_digits=15, + validators=[django.core.validators.MinValueValidator(0)], + ), + ), + ( + "reference", + models.CharField( + blank=True, help_text="Line item reference", max_length=100 + ), + ), + ( + "notes", + models.CharField( + blank=True, help_text="Line item notes", max_length=500 + ), + ), + ( + "order", + models.ForeignKey( + help_text="Sales Order", + on_delete=django.db.models.deletion.CASCADE, + related_name="lines", + to="order.salesorder", + ), + ), + ( + "part", + models.ForeignKey( + help_text="Part", + limit_choices_to={"salable": True}, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="sales_order_line_items", + to="part.part", + ), + ), + ], + options={ + "abstract": False, + "verbose_name": "Sales Order Line Item", + }, + ), + ] diff --git a/src/backend/InvenTree/order/migrations/0002_auto_20190604_2224.py b/src/backend/InvenTree/order/migrations/0002_auto_20190604_2224.py deleted file mode 100644 index 2565eae2a2..0000000000 --- a/src/backend/InvenTree/order/migrations/0002_auto_20190604_2224.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 2.2 on 2019-06-04 12:24 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorder', - name='URL', - field=models.URLField(blank=True, help_text='Link to external page'), - ), - migrations.AlterField( - model_name='purchaseorder', - name='description', - field=models.CharField(blank=True, help_text='Order description', max_length=250), - ), - migrations.AlterField( - model_name='purchaseorder', - name='supplier', - field=models.ForeignKey(help_text='Company', limit_choices_to={'is_supplier': True}, on_delete=django.db.models.deletion.CASCADE, related_name='Orders', to='company.Company'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0003_auto_20190604_2226.py b/src/backend/InvenTree/order/migrations/0003_auto_20190604_2226.py deleted file mode 100644 index 612166c2f7..0000000000 --- a/src/backend/InvenTree/order/migrations/0003_auto_20190604_2226.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-06-04 12:26 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0002_auto_20190604_2224'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorder', - name='supplier', - field=models.ForeignKey(help_text='Company', limit_choices_to={'is_supplier': True}, on_delete=django.db.models.deletion.CASCADE, related_name='purchase_orders', to='company.Company'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0004_purchaseorder_status.py b/src/backend/InvenTree/order/migrations/0004_purchaseorder_status.py deleted file mode 100644 index d1ad90eb38..0000000000 --- a/src/backend/InvenTree/order/migrations/0004_purchaseorder_status.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-06-04 12:52 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0003_auto_20190604_2226'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorder', - name='status', - field=models.PositiveIntegerField(choices=[(10, 'Pending'), (20, 'Placed'), (30, 'Complete'), (40, 'Cancelled'), (50, 'Lost'), (60, 'Returned')], default=10, help_text='Order status'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0005_purchaseorderlineitem_part.py b/src/backend/InvenTree/order/migrations/0005_purchaseorderlineitem_part.py deleted file mode 100644 index 61f0944156..0000000000 --- a/src/backend/InvenTree/order/migrations/0005_purchaseorderlineitem_part.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2 on 2019-06-05 10:36 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0005_auto_20190525_2356'), - ('order', '0004_purchaseorder_status'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorderlineitem', - name='part', - field=models.ForeignKey(blank=True, help_text='Supplier part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='orders', to='company.SupplierPart'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0006_auto_20190605_2056.py b/src/backend/InvenTree/order/migrations/0006_auto_20190605_2056.py deleted file mode 100644 index e938343d01..0000000000 --- a/src/backend/InvenTree/order/migrations/0006_auto_20190605_2056.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-06-05 10:56 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0005_auto_20190525_2356'), - ('order', '0005_purchaseorderlineitem_part'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='purchaseorderlineitem', - unique_together={('order', 'part')}, - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0007_auto_20190605_2138.py b/src/backend/InvenTree/order/migrations/0007_auto_20190605_2138.py deleted file mode 100644 index ce2119f258..0000000000 --- a/src/backend/InvenTree/order/migrations/0007_auto_20190605_2138.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-06-05 11:38 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0006_auto_20190605_2056'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorderlineitem', - name='part', - field=models.ForeignKey(blank=True, help_text='Supplier part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='order_line_items', to='company.SupplierPart'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0008_auto_20190605_2140.py b/src/backend/InvenTree/order/migrations/0008_auto_20190605_2140.py deleted file mode 100644 index 688c8cf15c..0000000000 --- a/src/backend/InvenTree/order/migrations/0008_auto_20190605_2140.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-06-05 11:40 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0007_auto_20190605_2138'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorderlineitem', - name='part', - field=models.ForeignKey(blank=True, help_text='Supplier part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='purchase_order_line_items', to='company.SupplierPart'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0009_auto_20190606_2133.py b/src/backend/InvenTree/order/migrations/0009_auto_20190606_2133.py deleted file mode 100644 index 0cadfe87fe..0000000000 --- a/src/backend/InvenTree/order/migrations/0009_auto_20190606_2133.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-06-06 11:33 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0008_auto_20190605_2140'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorder', - name='description', - field=models.CharField(help_text='Order description', max_length=250), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0010_purchaseorderlineitem_notes.py b/src/backend/InvenTree/order/migrations/0010_purchaseorderlineitem_notes.py deleted file mode 100644 index a38a564041..0000000000 --- a/src/backend/InvenTree/order/migrations/0010_purchaseorderlineitem_notes.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.2 on 2019-06-13 11:54 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0009_auto_20190606_2133'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorderlineitem', - name='notes', - field=models.CharField(blank=True, help_text='Line item notes', max_length=500), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0011_auto_20190615_1928.py b/src/backend/InvenTree/order/migrations/0011_auto_20190615_1928.py deleted file mode 100644 index cf6f1f61dc..0000000000 --- a/src/backend/InvenTree/order/migrations/0011_auto_20190615_1928.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 2.2.2 on 2019-06-15 09:28 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('order', '0010_purchaseorderlineitem_notes'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorder', - name='complete_date', - field=models.DateField(blank=True, null=True), - ), - migrations.AddField( - model_name='purchaseorder', - name='received_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0012_auto_20190617_1943.py b/src/backend/InvenTree/order/migrations/0012_auto_20190617_1943.py deleted file mode 100644 index b47d73d134..0000000000 --- a/src/backend/InvenTree/order/migrations/0012_auto_20190617_1943.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.2 on 2019-06-17 09:43 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0011_auto_20190615_1928'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorder', - name='creation_date', - field=models.DateField(blank=True, null=True), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0013_auto_20191118_2323.py b/src/backend/InvenTree/order/migrations/0013_auto_20191118_2323.py deleted file mode 100644 index 33db8f9ebd..0000000000 --- a/src/backend/InvenTree/order/migrations/0013_auto_20191118_2323.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.5 on 2019-11-18 23:23 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0012_auto_20190617_1943'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorderlineitem', - name='quantity', - field=models.DecimalField(decimal_places=5, default=1, help_text='Item quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0014_auto_20191118_2328.py b/src/backend/InvenTree/order/migrations/0014_auto_20191118_2328.py deleted file mode 100644 index 6020fddd87..0000000000 --- a/src/backend/InvenTree/order/migrations/0014_auto_20191118_2328.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.5 on 2019-11-18 23:28 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0013_auto_20191118_2323'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorderlineitem', - name='received', - field=models.DecimalField(decimal_places=5, default=0, help_text='Number of items received', max_digits=15), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0015_auto_20200201_2346.py b/src/backend/InvenTree/order/migrations/0015_auto_20200201_2346.py deleted file mode 100644 index f59a05fcfb..0000000000 --- a/src/backend/InvenTree/order/migrations/0015_auto_20200201_2346.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.9 on 2020-02-01 23:46 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0014_auto_20191118_2328'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorder', - name='notes', - field=models.TextField(blank=True, help_text='Order notes'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0016_purchaseorderattachment.py b/src/backend/InvenTree/order/migrations/0016_purchaseorderattachment.py deleted file mode 100644 index 750642f6f9..0000000000 --- a/src/backend/InvenTree/order/migrations/0016_purchaseorderattachment.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 2.2.9 on 2020-03-22 07:01 - -import InvenTree.models -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0015_auto_20200201_2346'), - ] - - operations = [ - migrations.CreateModel( - name='PurchaseOrderAttachment', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('attachment', models.FileField(help_text='Select file to attach', upload_to='attachments')), - ('comment', models.CharField(help_text='File comment', max_length=100)), - ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='order.PurchaseOrder')), - ], - options={ - 'abstract': False, - }, - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0017_auto_20200331_1000.py b/src/backend/InvenTree/order/migrations/0017_auto_20200331_1000.py deleted file mode 100644 index d548891aa4..0000000000 --- a/src/backend/InvenTree/order/migrations/0017_auto_20200331_1000.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2.10 on 2020-03-31 10:00 - -import InvenTree.fields -import django.core.validators -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0016_purchaseorderattachment'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorderlineitem', - name='quantity', - field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Item quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0018_auto_20200406_0151.py b/src/backend/InvenTree/order/migrations/0018_auto_20200406_0151.py deleted file mode 100644 index 1dfada77f6..0000000000 --- a/src/backend/InvenTree/order/migrations/0018_auto_20200406_0151.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.10 on 2020-04-06 01:51 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0017_auto_20200331_1000'), - ] - - operations = [ - migrations.RenameField( - model_name='purchaseorder', - old_name='URL', - new_name='link', - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0019_purchaseorder_supplier_reference.py b/src/backend/InvenTree/order/migrations/0019_purchaseorder_supplier_reference.py deleted file mode 100644 index cf9cd345bc..0000000000 --- a/src/backend/InvenTree/order/migrations/0019_purchaseorder_supplier_reference.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.10 on 2020-04-15 03:31 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0018_auto_20200406_0151'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorder', - name='supplier_reference', - field=models.CharField(blank=True, help_text='Supplier order reference', max_length=64), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0020_auto_20200420_0940.py b/src/backend/InvenTree/order/migrations/0020_auto_20200420_0940.py deleted file mode 100644 index 083c300df3..0000000000 --- a/src/backend/InvenTree/order/migrations/0020_auto_20200420_0940.py +++ /dev/null @@ -1,77 +0,0 @@ -# Generated by Django 3.0.5 on 2020-04-20 09:40 - -import InvenTree.fields -import InvenTree.models -from django.conf import settings -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0021_remove_supplierpart_manufacturer_name'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('order', '0019_purchaseorder_supplier_reference'), - ] - - operations = [ - migrations.CreateModel( - name='SalesOrder', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('reference', models.CharField(help_text='Order reference', max_length=64, unique=True)), - ('description', models.CharField(help_text='Order description', max_length=250)), - ('link', models.URLField(blank=True, help_text='Link to external page')), - ('creation_date', models.DateField(blank=True, null=True)), - ('status', models.PositiveIntegerField(choices=[(10, 'Pending'), (20, 'Placed'), (30, 'Complete'), (40, 'Cancelled'), (50, 'Lost'), (60, 'Returned')], default=10, help_text='Order status')), - ('issue_date', models.DateField(blank=True, null=True)), - ('complete_date', models.DateField(blank=True, null=True)), - ('notes', models.TextField(blank=True, help_text='Order notes')), - ('customer_reference', models.CharField(blank=True, help_text='Customer order reference code', max_length=64)), - ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), - ('customer', models.ForeignKey(help_text='Customer', limit_choices_to={True, 'is_supplier'}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sales_orders', to='company.Company')), - ], - options={ - 'abstract': False, - 'verbose_name': 'Sales Order', - }, - ), - migrations.AlterField( - model_name='purchaseorder', - name='supplier', - field=models.ForeignKey(help_text='Supplier', limit_choices_to={'is_supplier': True}, on_delete=django.db.models.deletion.CASCADE, related_name='purchase_orders', to='company.Company'), - ), - migrations.AlterField( - model_name='purchaseorder', - name='supplier_reference', - field=models.CharField(blank=True, help_text='Supplier order reference code', max_length=64), - ), - migrations.CreateModel( - name='SalesOrderLineItem', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('quantity', InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Item quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)])), - ('reference', models.CharField(blank=True, help_text='Line item reference', max_length=100)), - ('notes', models.CharField(blank=True, help_text='Line item notes', max_length=500)), - ('order', models.ForeignKey(help_text='Sales Order', on_delete=django.db.models.deletion.CASCADE, related_name='lines', to='order.SalesOrder')), - ], - options={ - 'abstract': False, - 'verbose_name': 'Sales Order Line Item', - }, - ), - migrations.CreateModel( - name='SalesOrderAttachment', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('attachment', models.FileField(help_text='Select file to attach', upload_to='attachments')), - ('comment', models.CharField(help_text='File comment', max_length=100)), - ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='order.SalesOrder')), - ], - options={ - 'abstract': False, - }, - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0021_auto_20200420_1010.py b/src/backend/InvenTree/order/migrations/0021_auto_20200420_1010.py deleted file mode 100644 index 0f8351b660..0000000000 --- a/src/backend/InvenTree/order/migrations/0021_auto_20200420_1010.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.0.5 on 2020-04-20 10:10 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0021_remove_supplierpart_manufacturer_name'), - ('order', '0020_auto_20200420_0940'), - ] - - operations = [ - migrations.AlterField( - model_name='salesorder', - name='customer', - field=models.ForeignKey(help_text='Customer', limit_choices_to={'is_customer': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sales_orders', to='company.Company'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0022_salesorderlineitem_part.py b/src/backend/InvenTree/order/migrations/0022_salesorderlineitem_part.py deleted file mode 100644 index 1ef32fba1b..0000000000 --- a/src/backend/InvenTree/order/migrations/0022_salesorderlineitem_part.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.0.5 on 2020-04-20 22:33 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0035_auto_20200406_0045'), - ('order', '0021_auto_20200420_1010'), - ] - - operations = [ - migrations.AddField( - model_name='salesorderlineitem', - name='part', - field=models.ForeignKey(help_text='Part', limit_choices_to={'salable': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sales_orders', to='part.Part'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0023_auto_20200420_2309.py b/src/backend/InvenTree/order/migrations/0023_auto_20200420_2309.py deleted file mode 100644 index 32d47f593a..0000000000 --- a/src/backend/InvenTree/order/migrations/0023_auto_20200420_2309.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.0.5 on 2020-04-20 23:09 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0035_auto_20200406_0045'), - ('order', '0022_salesorderlineitem_part'), - ] - - operations = [ - migrations.AlterField( - model_name='salesorderlineitem', - name='part', - field=models.ForeignKey(help_text='Part', limit_choices_to={'salable': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sales_order_line_items', to='part.Part'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0024_salesorderallocation.py b/src/backend/InvenTree/order/migrations/0024_salesorderallocation.py index d3a4623b20..31a83b0241 100644 --- a/src/backend/InvenTree/order/migrations/0024_salesorderallocation.py +++ b/src/backend/InvenTree/order/migrations/0024_salesorderallocation.py @@ -10,7 +10,7 @@ class Migration(migrations.Migration): dependencies = [ ('stock', '0030_auto_20200422_0015'), - ('order', '0023_auto_20200420_2309'), + ('order', '0001_squashed_0023_auto_20200420_2309'), ] operations = [ diff --git a/src/backend/InvenTree/order/migrations/0031_auto_20200426_0612.py b/src/backend/InvenTree/order/migrations/0031_auto_20200426_0612.py deleted file mode 100644 index aa1cd055ec..0000000000 --- a/src/backend/InvenTree/order/migrations/0031_auto_20200426_0612.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.0.5 on 2020-04-26 06:12 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0034_auto_20200426_0602'), - ('order', '0030_auto_20200426_0551'), - ] - - operations = [ - migrations.AlterField( - model_name='salesorderallocation', - name='item', - field=models.ForeignKey(help_text='Select stock item to allocate', limit_choices_to={'belongs_to': None, 'build_order': None, 'part__salable': True, 'sales_order': None}, on_delete=django.db.models.deletion.CASCADE, related_name='sales_order_allocations', to='stock.StockItem'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0031_auto_20200426_0612_squashed_0112_alter_salesorderlineitem_part.py b/src/backend/InvenTree/order/migrations/0031_auto_20200426_0612_squashed_0112_alter_salesorderlineitem_part.py new file mode 100644 index 0000000000..39aa8f98b2 --- /dev/null +++ b/src/backend/InvenTree/order/migrations/0031_auto_20200426_0612_squashed_0112_alter_salesorderlineitem_part.py @@ -0,0 +1,1075 @@ +# Generated by Django 5.2.17 on 2026-09-09 00:46 + +import InvenTree.fields +import InvenTree.validators +import common.currency +import django.core.validators +import django.db.models.deletion +import djmoney.models.fields +import djmoney.models.validators +import generic.states.fields +import generic.states.validators +import mptt.fields +import order.status_codes +import order.validators +from django.conf import settings +from django.db import migrations, models + + +def update_order_references(order_model, prefix): + """Update all references of the given model, with the specified prefix.""" + n = 0 + + for order in order_model.objects.all(): + if not order.reference.startswith(prefix): + order.reference = prefix + order.reference + order.save() + + n += 1 + + return n + + +def update_salesorder_reference(apps, schema_editor): + """Migrate the reference pattern for the SalesOrder model.""" + InvenTreeSetting = apps.get_model('common', 'inventreesetting') + + try: + prefix = InvenTreeSetting.objects.get(key='SALESORDER_REFERENCE_PREFIX').value + except Exception: + prefix = 'SO-' + + pattern = prefix + '{ref:04d}' + + try: + setting = InvenTreeSetting.objects.get(key='SALESORDER_REFERENCE_PATTERN') + setting.value = pattern + setting.save() + except InvenTreeSetting.DoesNotExist: + InvenTreeSetting.objects.create(key='SALESORDER_REFERENCE_PATTERN', value=pattern) + + SalesOrder = apps.get_model('order', 'salesorder') + update_order_references(SalesOrder, prefix) + + +def update_purchaseorder_reference(apps, schema_editor): + """Migrate the reference pattern for the PurchaseOrder model.""" + InvenTreeSetting = apps.get_model('common', 'inventreesetting') + + try: + prefix = InvenTreeSetting.objects.get(key='PURCHASEORDER_REFERENCE_PREFIX').value + except Exception: + prefix = 'PO-' + + pattern = prefix + '{ref:04d}' + + try: + setting = InvenTreeSetting.objects.get(key='PURCHASEORDER_REFERENCE_PATTERN') + setting.value = pattern + setting.save() + except InvenTreeSetting.DoesNotExist: + InvenTreeSetting.objects.create(key='PURCHASEORDER_REFERENCE_PATTERN', value=pattern) + + PurchaseOrder = apps.get_model('order', 'purchaseorder') + update_order_references(PurchaseOrder, prefix) + + +class Migration(migrations.Migration): + + replaces = [('order', '0031_auto_20200426_0612'), ('order', '0032_auto_20200427_0044'), ('order', '0033_auto_20200512_1033'), ('order', '0034_auto_20200512_1054'), ('order', '0035_auto_20200513_0016'), ('order', '0036_auto_20200831_0912'), ('order', '0037_auto_20201110_0911'), ('order', '0038_auto_20201112_1737'), ('order', '0039_auto_20201112_2203'), ('order', '0040_salesorder_target_date'), ('order', '0041_auto_20210114_1728'), ('order', '0042_auto_20210310_1619'), ('order', '0043_auto_20210330_0013'), ('order', '0044_auto_20210404_2016'), ('order', '0045_auto_20210504_1946'), ('order', '0046_purchaseorderlineitem_destination'), ('order', '0047_auto_20210701_0509'), ('order', '0048_auto_20210702_2321'), ('order', '0049_alter_purchaseorderlineitem_unique_together'), ('order', '0050_alter_purchaseorderlineitem_destination'), ('order', '0051_auto_20211014_0623'), ('order', '0052_auto_20211014_0631'), ('order', '0053_auto_20211128_0151'), ('order', '0053_salesordershipment'), ('order', '0054_salesorderallocation_shipment'), ('order', '0055_auto_20211025_0645'), ('order', '0056_alter_salesorderallocation_shipment'), ('order', '0057_salesorderlineitem_shipped'), ('order', '0058_auto_20211126_1210'), ('order', '0059_salesordershipment_tracking_number'), ('order', '0060_auto_20211129_1339'), ('order', '0054_auto_20211201_2139'), ('order', '0061_merge_0054_auto_20211201_2139_0060_auto_20211129_1339'), ('order', '0062_auto_20220228_0321'), ('order', '0063_alter_purchaseorderlineitem_unique_together'), ('order', '0064_purchaseorderextraline_salesorderextraline'), ('order', '0065_alter_purchaseorderlineitem_part'), ('order', '0066_alter_purchaseorder_supplier'), ('order', '0067_auto_20220516_1120'), ('order', '0068_alter_salesorderallocation_unique_together'), ('order', '0069_auto_20220524_0508'), ('order', '0070_auto_20220620_0728'), ('order', '0071_auto_20220628_0133'), ('order', '0072_alter_salesorder_reference'), ('order', '0073_alter_purchaseorder_reference'), ('order', '0074_auto_20220709_0108'), ('order', '0075_auto_20221110_0108'), ('order', '0076_auto_20221111_0153'), ('order', '0077_auto_20230129_0154'), ('order', '0078_auto_20230304_0721'), ('order', '0079_auto_20230304_0904'), ('order', '0080_auto_20230317_0816'), ('order', '0081_auto_20230314_0725'), ('order', '0082_auto_20230314_1259'), ('order', '0083_returnorderextraline'), ('order', '0084_auto_20230321_1111'), ('order', '0085_auto_20230322_1056'), ('order', '0086_auto_20230323_1108'), ('order', '0087_alter_salesorder_status'), ('order', '0088_auto_20230403_1402'), ('order', '0089_auto_20230404_0030'), ('order', '0090_auto_20230412_1752'), ('order', '0091_auto_20230419_0037'), ('order', '0092_auto_20230419_0250'), ('order', '0093_auto_20230426_0248'), ('order', '0094_auto_20230514_2331'), ('order', '0095_salesordershipment_delivery_date'), ('order', '0096_alter_returnorderlineitem_outcome'), ('order', '0097_auto_20230529_0107'), ('order', '0098_auto_20231024_1844'), ('order', '0099_alter_salesorder_status'), ('order', '0100_remove_returnorderattachment_order_and_more'), ('order', '0101_purchaseorder_status_custom_key_and_more'), ('order', '0102_purchaseorder_destination_and_more'), ('order', '0103_alter_salesorderallocation_shipment'), ('order', '0104_alter_returnorderlineitem_quantity'), ('order', '0105_auto_20241128_0431'), ('order', '0106_purchaseorder_start_date_returnorder_start_date_and_more'), ('order', '0107_auto_20250221_1241'), ('order', '0108_alter_purchaseorder_link_and_more_squashed_0109_alter_purchaseorderextraline_link_and_more'), ('order', '0109_salesorder_issue_date'), ('order', '0110_salesordershipment_barcode_data_and_more'), ('order', '0111_purchaseorderlineitem_build_order'), ('order', '0112_alter_salesorderlineitem_part')] + + dependencies = [ + ('build', '0057_build_external'), + ('common', '0004_inventreesetting'), + ('common', '0018_projectcode'), + ('company', '0023_auto_20200808_0715'), + ('company', '0032_auto_20210403_1837'), + ('company', '0040_alter_company_currency'), + ('company', '0042_supplierpricebreak_updated'), + ('company', '0054_companyattachment'), + ('company', '0065_remove_company_address'), + ('order', '0030_auto_20200426_0551'), + ('part', '0035_auto_20200406_0045'), + ('part', '0063_bomitem_inherited'), + ('part', '0079_alter_part_notes'), + ('part', '0142_remove_part_last_stocktake_remove_partstocktake_note_and_more'), + ('stock', '0034_auto_20200426_0602'), + ('stock', '0054_remove_stockitem_build_order'), + ('stock', '0058_stockitem_packaging'), + ('stock', '0063_auto_20210511_2343'), + ('stock', '0065_auto_20210701_0509'), + ('stock', '0077_alter_stockitem_notes'), + ('stock', '0095_stocklocation_external'), + ('stock', '0113_stockitem_status_custom_key_and_more'), + ('users', '0005_owner_model'), + ('users', '0006_alter_ruleset_name'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='salesorder', + name='target_date', + field=models.DateField(blank=True, help_text='Target date for order completion. Order will be overdue after this date.', null=True, verbose_name='Target completion date'), + ), + migrations.AddField( + model_name='purchaseorder', + name='target_date', + field=models.DateField(blank=True, help_text='Expected date for order delivery. Order will be overdue after this date.', null=True, verbose_name='Target Delivery Date'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='complete_date', + field=models.DateField(blank=True, help_text='Date order was completed', null=True, verbose_name='Completion Date'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='issue_date', + field=models.DateField(blank=True, help_text='Date order was issued', null=True, verbose_name='Issue Date'), + ), + migrations.AddField( + model_name='purchaseorder', + name='responsible', + field=models.ForeignKey(blank=True, help_text='User or group responsible for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='users.owner', verbose_name='Responsible'), + ), + migrations.AddField( + model_name='salesorder', + name='responsible', + field=models.ForeignKey(blank=True, help_text='User or group responsible for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='users.owner', verbose_name='Responsible'), + ), + migrations.AlterUniqueTogether( + name='salesorderlineitem', + unique_together=set(), + ), + migrations.AlterField( + model_name='purchaseorder', + name='created_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='creation_date', + field=models.DateField(blank=True, null=True, verbose_name='Creation Date'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='description', + field=models.CharField(help_text='Order description', max_length=250, verbose_name='Description'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='link', + field=models.URLField(blank=True, help_text='Link to external page', verbose_name='Link'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='notes', + field=models.TextField(blank=True, help_text='Order notes', verbose_name='Notes'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='received_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='received by'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='reference', + field=models.CharField(default='PO', help_text='Order reference', max_length=64, unique=True, verbose_name='Reference'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='supplier', + field=models.ForeignKey(help_text='Company from which the items are being ordered', limit_choices_to={'is_supplier': True}, on_delete=django.db.models.deletion.CASCADE, related_name='purchase_orders', to='company.company', verbose_name='Supplier'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='supplier_reference', + field=models.CharField(blank=True, help_text='Supplier order reference code', max_length=64, verbose_name='Supplier Reference'), + ), + migrations.AlterField( + model_name='purchaseorderattachment', + name='attachment', + field=models.FileField(help_text='Select file to attach', upload_to='attachments', verbose_name='Attachment'), + ), + migrations.AlterField( + model_name='purchaseorderattachment', + name='comment', + field=models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment'), + ), + migrations.AddField( + model_name='purchaseorderattachment', + name='upload_date', + field=models.DateField(auto_now_add=True, null=True, verbose_name='upload date'), + ), + migrations.AddField( + model_name='purchaseorderattachment', + name='user', + field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User'), + ), + migrations.AlterField( + model_name='purchaseorderlineitem', + name='notes', + field=models.CharField(blank=True, help_text='Line item notes', max_length=500, verbose_name='Notes'), + ), + migrations.AlterField( + model_name='purchaseorderlineitem', + name='order', + field=models.ForeignKey(help_text='Purchase Order', on_delete=django.db.models.deletion.CASCADE, related_name='lines', to='order.purchaseorder', verbose_name='Order'), + ), + migrations.AlterField( + model_name='purchaseorderlineitem', + name='part', + field=models.ForeignKey(blank=True, help_text='Supplier part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='purchase_order_line_items', to='company.supplierpart', verbose_name='Part'), + ), + migrations.AlterField( + model_name='purchaseorderlineitem', + name='quantity', + field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Item quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'), + ), + migrations.AlterField( + model_name='purchaseorderlineitem', + name='received', + field=models.DecimalField(decimal_places=5, default=0, help_text='Number of items received', max_digits=15, verbose_name='Received'), + ), + migrations.AlterField( + model_name='purchaseorderlineitem', + name='reference', + field=models.CharField(blank=True, help_text='Line item reference', max_length=100, verbose_name='Reference'), + ), + migrations.AlterField( + model_name='salesorder', + name='created_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), + ), + migrations.AlterField( + model_name='salesorder', + name='creation_date', + field=models.DateField(blank=True, null=True, verbose_name='Creation Date'), + ), + migrations.AlterField( + model_name='salesorder', + name='customer', + field=models.ForeignKey(help_text='Company to which the items are being sold', limit_choices_to={'is_customer': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sales_orders', to='company.company', verbose_name='Customer'), + ), + migrations.AlterField( + model_name='salesorder', + name='customer_reference', + field=models.CharField(blank=True, help_text='Customer order reference code', max_length=64, verbose_name='Customer Reference '), + ), + migrations.AlterField( + model_name='salesorder', + name='description', + field=models.CharField(help_text='Order description', max_length=250, verbose_name='Description'), + ), + migrations.AlterField( + model_name='salesorder', + name='link', + field=models.URLField(blank=True, help_text='Link to external page', verbose_name='Link'), + ), + migrations.AlterField( + model_name='salesorder', + name='notes', + field=models.TextField(blank=True, help_text='Order notes', verbose_name='Notes'), + ), + migrations.AlterField( + model_name='salesorder', + name='reference', + field=models.CharField(default='SO', help_text='Order reference', max_length=64, unique=True, verbose_name='Reference'), + ), + migrations.AlterField( + model_name='salesorder', + name='shipment_date', + field=models.DateField(blank=True, null=True, verbose_name='Shipment Date'), + ), + migrations.AlterField( + model_name='salesorder', + name='shipped_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='shipped by'), + ), + migrations.AlterField( + model_name='salesorder', + name='status', + field=models.PositiveIntegerField(choices=[(10, 'Pending'), (20, 'Shipped'), (40, 'Cancelled'), (50, 'Lost'), (60, 'Returned')], default=10, help_text='Purchase order status', verbose_name='Status'), + ), + migrations.AlterField( + model_name='salesorderallocation', + name='item', + field=models.ForeignKey(help_text='Select stock item to allocate', limit_choices_to={'belongs_to': None, 'part__salable': True, 'sales_order': None}, on_delete=django.db.models.deletion.CASCADE, related_name='sales_order_allocations', to='stock.stockitem', verbose_name='Item'), + ), + migrations.AlterField( + model_name='salesorderallocation', + name='line', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='order.salesorderlineitem', verbose_name='Line'), + ), + migrations.AlterField( + model_name='salesorderallocation', + name='quantity', + field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Enter stock allocation quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'), + ), + migrations.AlterField( + model_name='salesorderattachment', + name='attachment', + field=models.FileField(help_text='Select file to attach', upload_to='attachments', verbose_name='Attachment'), + ), + migrations.AlterField( + model_name='salesorderattachment', + name='comment', + field=models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment'), + ), + migrations.AddField( + model_name='salesorderattachment', + name='upload_date', + field=models.DateField(auto_now_add=True, null=True, verbose_name='upload date'), + ), + migrations.AddField( + model_name='salesorderattachment', + name='user', + field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User'), + ), + migrations.AlterField( + model_name='salesorderlineitem', + name='notes', + field=models.CharField(blank=True, help_text='Line item notes', max_length=500, verbose_name='Notes'), + ), + migrations.AlterField( + model_name='salesorderlineitem', + name='order', + field=models.ForeignKey(help_text='Sales Order', on_delete=django.db.models.deletion.CASCADE, related_name='lines', to='order.salesorder', verbose_name='Order'), + ), + migrations.AlterField( + model_name='salesorderlineitem', + name='part', + field=models.ForeignKey(help_text='Part', limit_choices_to={'salable': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sales_order_line_items', to='part.part', verbose_name='Part'), + ), + migrations.AlterField( + model_name='salesorderlineitem', + name='quantity', + field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Item quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'), + ), + migrations.AlterField( + model_name='salesorderlineitem', + name='reference', + field=models.CharField(blank=True, help_text='Line item reference', max_length=100, verbose_name='Reference'), + ), + migrations.AddField( + model_name='purchaseorderlineitem', + name='purchase_price', + field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=4, default_currency=common.currency.currency_code_default, help_text='Unit purchase price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Purchase Price'), + ), + migrations.AddField( + model_name='purchaseorderlineitem', + name='purchase_price_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3), + ), + migrations.AddField( + model_name='salesorderlineitem', + name='sale_price', + field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=4, default_currency=common.currency.currency_code_default, help_text='Unit sale price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Sale Price'), + ), + migrations.AddField( + model_name='salesorderlineitem', + name='sale_price_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3), + ), + migrations.AddField( + model_name='purchaseorderlineitem', + name='destination', + field=mptt.fields.TreeForeignKey(blank=True, help_text='Where does the Purchaser want this item to be stored?', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='po_lines', to='stock.stocklocation', verbose_name='Destination'), + ), + migrations.AddField( + model_name='purchaseorder', + name='reference_int', + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name='salesorder', + name='reference_int', + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name='purchaseorderattachment', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=2000, null=True, verbose_name='Link'), + ), + migrations.AddField( + model_name='salesorderattachment', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=2000, null=True, verbose_name='Link'), + ), + migrations.AlterField( + model_name='purchaseorderattachment', + name='attachment', + field=models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to='attachments', verbose_name='Attachment'), + ), + migrations.AlterField( + model_name='salesorderattachment', + name='attachment', + field=models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to='attachments', verbose_name='Attachment'), + ), + migrations.CreateModel( + name='SalesOrderShipment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('shipment_date', models.DateField(blank=True, help_text='Date of shipment', null=True, verbose_name='Shipment Date')), + ('reference', models.CharField(default='1', help_text='Shipment reference', max_length=100, verbose_name='Reference')), + ('notes', models.TextField(blank=True, help_text='Shipment notes', verbose_name='Notes')), + ('checked_by', models.ForeignKey(blank=True, help_text='User who checked this shipment', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Checked By')), + ('order', models.ForeignKey(help_text='Sales Order', on_delete=django.db.models.deletion.CASCADE, related_name='shipments', to='order.salesorder', verbose_name='Order')), + ], + options={ + 'verbose_name': 'Sales Order Shipment', + }, + ), + migrations.AddField( + model_name='salesorderallocation', + name='shipment', + field=models.ForeignKey(blank=True, help_text='Sales order shipment reference', null=True, on_delete=django.db.models.deletion.CASCADE, to='order.salesordershipment', verbose_name='Shipment'), + ), + migrations.AlterField( + model_name='salesorderallocation', + name='shipment', + field=models.ForeignKey(help_text='Sales order shipment reference', on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='order.salesordershipment', verbose_name='Shipment'), + ), + migrations.AddField( + model_name='salesorderlineitem', + name='shipped', + field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=0, help_text='Shipped quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Shipped'), + ), + migrations.AddField( + model_name='salesordershipment', + name='tracking_number', + field=models.CharField(blank=True, help_text='Shipment tracking information', max_length=100, verbose_name='Tracking Number'), + ), + migrations.AlterField( + model_name='salesordershipment', + name='reference', + field=models.CharField(default='1', help_text='Shipment number', max_length=100, verbose_name='Shipment'), + ), + migrations.AlterUniqueTogether( + name='salesordershipment', + unique_together={('order', 'reference')}, + ), + migrations.AlterField( + model_name='purchaseorder', + name='reference_int', + field=models.BigIntegerField(default=0), + ), + migrations.AlterField( + model_name='salesorder', + name='reference_int', + field=models.BigIntegerField(default=0), + ), + migrations.AddField( + model_name='purchaseorderlineitem', + name='target_date', + field=models.DateField(blank=True, help_text='Target shipping date for this line item', null=True, verbose_name='Target Date'), + ), + migrations.AddField( + model_name='salesorderlineitem', + name='target_date', + field=models.DateField(blank=True, help_text='Target shipping date for this line item', null=True, verbose_name='Target Date'), + ), + migrations.AlterUniqueTogether( + name='purchaseorderlineitem', + unique_together=set(), + ), + migrations.CreateModel( + name='SalesOrderExtraLine', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity', InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Item quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity')), + ('reference', models.CharField(blank=True, help_text='Line item reference', max_length=100, verbose_name='Reference')), + ('notes', models.CharField(blank=True, help_text='Line item notes', max_length=500, verbose_name='Notes')), + ('target_date', models.DateField(blank=True, help_text='Target shipping date for this line item', null=True, verbose_name='Target Date')), + ('context', models.JSONField(blank=True, help_text='Additional context for this line', null=True, verbose_name='Context')), + ('price_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('price', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=4, default_currency=common.currency.currency_code_default, help_text='Unit price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price')), + ('order', models.ForeignKey(help_text='Sales Order', on_delete=django.db.models.deletion.CASCADE, related_name='extra_lines', to='order.salesorder', verbose_name='Order')), + ], + options={ + 'abstract': False, + 'verbose_name': 'Sales Order Extra Line', + }, + ), + migrations.CreateModel( + name='PurchaseOrderExtraLine', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity', InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Item quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity')), + ('reference', models.CharField(blank=True, help_text='Line item reference', max_length=100, verbose_name='Reference')), + ('notes', models.CharField(blank=True, help_text='Line item notes', max_length=500, verbose_name='Notes')), + ('target_date', models.DateField(blank=True, help_text='Target shipping date for this line item', null=True, verbose_name='Target Date')), + ('context', models.JSONField(blank=True, help_text='Additional context for this line', null=True, verbose_name='Context')), + ('price_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('price', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=4, default_currency=common.currency.currency_code_default, help_text='Unit price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price')), + ('order', models.ForeignKey(help_text='Purchase Order', on_delete=django.db.models.deletion.CASCADE, related_name='extra_lines', to='order.purchaseorder', verbose_name='Order')), + ], + options={ + 'abstract': False, + 'verbose_name': 'Purchase Order Extra Line', + }, + ), + migrations.AlterField( + model_name='purchaseorderlineitem', + name='part', + field=models.ForeignKey(help_text='Supplier part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='purchase_order_line_items', to='company.supplierpart', verbose_name='Part'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='supplier', + field=models.ForeignKey(help_text='Company from which the items are being ordered', limit_choices_to={'is_supplier': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='purchase_orders', to='company.company', verbose_name='Supplier'), + ), + migrations.AddField( + model_name='purchaseorder', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AddField( + model_name='salesorder', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AlterUniqueTogether( + name='salesorderallocation', + unique_together=set(), + ), + migrations.AddField( + model_name='salesordershipment', + name='invoice_number', + field=models.CharField(blank=True, help_text='Reference number for associated invoice', max_length=100, verbose_name='Invoice Number'), + ), + migrations.AddField( + model_name='salesordershipment', + name='link', + field=models.URLField(blank=True, help_text='Link to external page', verbose_name='Link'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='notes', + field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Order notes', max_length=50000, null=True, verbose_name='Notes'), + ), + migrations.AlterField( + model_name='salesorder', + name='notes', + field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Order notes', max_length=50000, null=True, verbose_name='Notes'), + ), + migrations.AlterField( + model_name='salesordershipment', + name='notes', + field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Shipment notes', max_length=50000, null=True, verbose_name='Notes'), + ), + migrations.AlterField( + model_name='salesorderallocation', + name='item', + field=models.ForeignKey(help_text='Select stock item to allocate', limit_choices_to={'belongs_to': None, 'part__salable': True, 'part__virtual': False, 'sales_order': None}, on_delete=django.db.models.deletion.CASCADE, related_name='sales_order_allocations', to='stock.stockitem', verbose_name='Item'), + ), + migrations.AlterField( + model_name='salesorderlineitem', + name='part', + field=models.ForeignKey(help_text='Part', limit_choices_to={'salable': True, 'virtual': False}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sales_order_line_items', to='part.part', verbose_name='Part'), + ), + migrations.AlterField( + model_name='salesorder', + name='reference', + field=models.CharField(default=order.validators.generate_next_sales_order_reference, help_text='Order reference', max_length=64, unique=True, validators=[order.validators.validate_sales_order_reference], verbose_name='Reference'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='reference', + field=models.CharField(default=order.validators.generate_next_purchase_order_reference, help_text='Order reference', max_length=64, unique=True, validators=[order.validators.validate_purchase_order_reference], verbose_name='Reference'), + ), + migrations.RunPython(code=update_salesorder_reference, reverse_code=migrations.RunPython.noop), + migrations.RunPython(code=update_purchaseorder_reference, reverse_code=migrations.RunPython.noop), + migrations.AlterField( + model_name='purchaseorder', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link'), + ), + migrations.AlterField( + model_name='salesorder', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link'), + ), + migrations.AlterField( + model_name='salesordershipment', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link'), + ), + migrations.AlterField( + model_name='purchaseorderextraline', + name='price', + field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Unit price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price'), + ), + migrations.AlterField( + model_name='purchaseorderlineitem', + name='purchase_price', + field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Unit purchase price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Purchase Price'), + ), + migrations.AlterField( + model_name='salesorderextraline', + name='price', + field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Unit price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price'), + ), + migrations.AlterField( + model_name='salesorderlineitem', + name='sale_price', + field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Unit sale price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Sale Price'), + ), + migrations.AlterField( + model_name='purchaseorderextraline', + name='target_date', + field=models.DateField(blank=True, help_text='Target date for this line item (leave blank to use the target date from the order)', null=True, verbose_name='Target Date'), + ), + migrations.AlterField( + model_name='purchaseorderlineitem', + name='target_date', + field=models.DateField(blank=True, help_text='Target date for this line item (leave blank to use the target date from the order)', null=True, verbose_name='Target Date'), + ), + migrations.AlterField( + model_name='salesorderextraline', + name='target_date', + field=models.DateField(blank=True, help_text='Target date for this line item (leave blank to use the target date from the order)', null=True, verbose_name='Target Date'), + ), + migrations.AlterField( + model_name='salesorderlineitem', + name='target_date', + field=models.DateField(blank=True, help_text='Target date for this line item (leave blank to use the target date from the order)', null=True, verbose_name='Target Date'), + ), + migrations.AddField( + model_name='purchaseorder', + name='total_price', + field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Total price for this order', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Total Price'), + ), + migrations.AddField( + model_name='purchaseorder', + name='total_price_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3), + ), + migrations.AddField( + model_name='salesorder', + name='total_price', + field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Total price for this order', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Total Price'), + ), + migrations.AddField( + model_name='salesorder', + name='total_price_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3), + ), + migrations.AddField( + model_name='purchaseorderextraline', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AddField( + model_name='purchaseorderlineitem', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AddField( + model_name='salesorderextraline', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AddField( + model_name='salesorderlineitem', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AddField( + model_name='salesordershipment', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.CreateModel( + name='ReturnOrder', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('metadata', models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata')), + ('reference_int', models.BigIntegerField(default=0)), + ('description', models.CharField(blank=True, help_text='Order description (optional)', max_length=250, verbose_name='Description')), + ('link', InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link')), + ('creation_date', models.DateField(blank=True, null=True, verbose_name='Creation Date')), + ('notes', InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes')), + ('reference', models.CharField(default=order.validators.generate_next_return_order_reference, help_text='Return Order reference', max_length=64, unique=True, validators=[order.validators.validate_return_order_reference], verbose_name='Reference')), + ('status', generic.states.fields.InvenTreeCustomStatusModelField(choices=[(10, 'Pending'), (20, 'In Progress'), (25, 'On Hold'), (30, 'Complete'), (40, 'Cancelled')], default=10, help_text='Return order status', validators=[generic.states.validators.CustomStatusCodeValidator(status_class=order.status_codes.ReturnOrderStatus)], verbose_name='Status')), + ('customer_reference', models.CharField(blank=True, help_text='Customer order reference code', max_length=64, verbose_name='Customer Reference ')), + ('issue_date', models.DateField(blank=True, help_text='Date order was issued', null=True, verbose_name='Issue Date')), + ('complete_date', models.DateField(blank=True, help_text='Date order was completed', null=True, verbose_name='Completion Date')), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), + ('customer', models.ForeignKey(help_text='Company from which items are being returned', limit_choices_to={'is_customer': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sales_orders', to='company.company', verbose_name='Customer')), + ('responsible', models.ForeignKey(blank=True, help_text='User or group responsible for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='users.owner', verbose_name='Responsible')), + ('contact', models.ForeignKey(blank=True, help_text='Point of contact for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='company.contact', verbose_name='Contact')), + ('target_date', models.DateField(blank=True, help_text='Expected date for order delivery. Order will be overdue after this date.', null=True, verbose_name='Target Date')), + ('total_price', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Total price for this order', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Total Price')), + ('total_price_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True)), + ('barcode_data', models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data')), + ('barcode_hash', models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash')), + ('project_code', models.ForeignKey(blank=True, help_text='Select project code for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.projectcode', verbose_name='Project Code')), + ('order_currency', models.CharField(blank=True, help_text='Currency for this order (leave blank to use company default)', max_length=3, null=True, validators=[InvenTree.validators.validate_currency_code], verbose_name='Order Currency')), + ('address', models.ForeignKey(blank=True, help_text='Company address for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='company.address', verbose_name='Address')), + ('status_custom_key', generic.states.fields.ExtraInvenTreeCustomStatusModelField(blank=True, default=None, help_text='Additional status information for this item', null=True, validators=[generic.states.validators.CustomStatusCodeValidator(status_class=order.status_codes.ReturnOrderStatus)], verbose_name='Custom status key')), + ], + options={ + 'abstract': False, + 'verbose_name': 'Return Order', + }, + ), + migrations.AlterField( + model_name='salesorder', + name='customer', + field=models.ForeignKey(help_text='Company to which the items are being sold', limit_choices_to={'is_customer': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='return_orders', to='company.company', verbose_name='Customer'), + ), + migrations.CreateModel( + name='ReturnOrderAttachment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('attachment', models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to='attachments', verbose_name='Attachment')), + ('link', InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=2000, null=True, verbose_name='Link')), + ('comment', models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment')), + ('upload_date', models.DateField(auto_now_add=True, null=True, verbose_name='upload date')), + ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='order.returnorder')), + ('user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='purchaseorder', + name='contact', + field=models.ForeignKey(blank=True, help_text='Point of contact for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='company.contact', verbose_name='Contact'), + ), + migrations.AddField( + model_name='salesorder', + name='contact', + field=models.ForeignKey(blank=True, help_text='Point of contact for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='company.contact', verbose_name='Contact'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='target_date', + field=models.DateField(blank=True, help_text='Expected date for order delivery. Order will be overdue after this date.', null=True, verbose_name='Target Date'), + ), + migrations.AlterField( + model_name='salesorder', + name='target_date', + field=models.DateField(blank=True, help_text='Expected date for order delivery. Order will be overdue after this date.', null=True, verbose_name='Target Date'), + ), + migrations.AddField( + model_name='purchaseorderextraline', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link'), + ), + migrations.AddField( + model_name='purchaseorderlineitem', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link'), + ), + migrations.AddField( + model_name='salesorderextraline', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link'), + ), + migrations.AddField( + model_name='salesorderlineitem', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link'), + ), + migrations.AddField( + model_name='purchaseorder', + name='barcode_data', + field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), + ), + migrations.AddField( + model_name='purchaseorder', + name='barcode_hash', + field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), + ), + migrations.AddField( + model_name='salesorder', + name='barcode_data', + field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), + ), + migrations.AddField( + model_name='salesorder', + name='barcode_hash', + field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='description', + field=models.CharField(blank=True, help_text='Order description (optional)', max_length=250, verbose_name='Description'), + ), + migrations.AlterField( + model_name='salesorder', + name='description', + field=models.CharField(blank=True, help_text='Order description (optional)', max_length=250, verbose_name='Description'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='notes', + field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), + ), + migrations.AlterField( + model_name='salesorder', + name='notes', + field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), + ), + migrations.AlterField( + model_name='salesordershipment', + name='notes', + field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), + ), + migrations.AddField( + model_name='purchaseorder', + name='project_code', + field=models.ForeignKey(blank=True, help_text='Select project code for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.projectcode', verbose_name='Project Code'), + ), + migrations.AddField( + model_name='salesorder', + name='project_code', + field=models.ForeignKey(blank=True, help_text='Select project code for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.projectcode', verbose_name='Project Code'), + ), + migrations.AddField( + model_name='purchaseorder', + name='order_currency', + field=models.CharField(blank=True, help_text='Currency for this order (leave blank to use company default)', max_length=3, null=True, validators=[InvenTree.validators.validate_currency_code], verbose_name='Order Currency'), + ), + migrations.AddField( + model_name='salesorder', + name='order_currency', + field=models.CharField(blank=True, help_text='Currency for this order (leave blank to use company default)', max_length=3, null=True, validators=[InvenTree.validators.validate_currency_code], verbose_name='Order Currency'), + ), + migrations.AddField( + model_name='purchaseorderextraline', + name='description', + field=models.CharField(blank=True, help_text='Line item description (optional)', max_length=250, verbose_name='Description'), + ), + migrations.AddField( + model_name='salesorderextraline', + name='description', + field=models.CharField(blank=True, help_text='Line item description (optional)', max_length=250, verbose_name='Description'), + ), + migrations.AddField( + model_name='salesordershipment', + name='delivery_date', + field=models.DateField(blank=True, help_text='Date of delivery of shipment', null=True, verbose_name='Delivery Date'), + ), + migrations.AddField( + model_name='purchaseorder', + name='address', + field=models.ForeignKey(blank=True, help_text='Company address for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='company.address', verbose_name='Address'), + ), + migrations.AddField( + model_name='salesorder', + name='address', + field=models.ForeignKey(blank=True, help_text='Company address for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='company.address', verbose_name='Address'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='total_price_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='purchaseorderextraline', + name='price_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='purchaseorderlineitem', + name='purchase_price_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.CreateModel( + name='ReturnOrderExtraLine', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity', InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Item quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity')), + ('reference', models.CharField(blank=True, help_text='Line item reference', max_length=100, verbose_name='Reference')), + ('notes', models.CharField(blank=True, help_text='Line item notes', max_length=500, verbose_name='Notes')), + ('target_date', models.DateField(blank=True, help_text='Target date for this line item (leave blank to use the target date from the order)', null=True, verbose_name='Target Date')), + ('context', models.JSONField(blank=True, help_text='Additional context for this line', null=True, verbose_name='Context')), + ('price_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True)), + ('price', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Unit price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price')), + ('order', models.ForeignKey(help_text='Return Order', on_delete=django.db.models.deletion.CASCADE, related_name='extra_lines', to='order.returnorder', verbose_name='Order')), + ('metadata', models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata')), + ('link', InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link')), + ('description', models.CharField(blank=True, help_text='Line item description (optional)', max_length=250, verbose_name='Description')), + ], + options={ + 'abstract': False, + 'verbose_name': 'Return Order Extra Line', + }, + ), + migrations.CreateModel( + name='ReturnOrderLineItem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity', models.DecimalField(decimal_places=5, default=1, help_text='Quantity to return', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity')), + ('reference', models.CharField(blank=True, help_text='Line item reference', max_length=100, verbose_name='Reference')), + ('notes', models.CharField(blank=True, help_text='Line item notes', max_length=500, verbose_name='Notes')), + ('target_date', models.DateField(blank=True, help_text='Target date for this line item (leave blank to use the target date from the order)', null=True, verbose_name='Target Date')), + ('received_date', models.DateField(blank=True, help_text='The date this return item was received', null=True, verbose_name='Received Date')), + ('outcome', generic.states.fields.InvenTreeCustomStatusModelField(choices=[(10, 'Pending'), (20, 'Return'), (30, 'Repair'), (40, 'Replace'), (50, 'Refund'), (60, 'Reject')], default=10, help_text='Outcome for this line item', validators=[generic.states.validators.CustomStatusCodeValidator(status_class=order.status_codes.ReturnOrderLineStatus)], verbose_name='Outcome')), + ('price_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True)), + ('price', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Cost associated with return or repair for this line item', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price')), + ('link', InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link')), + ('item', models.ForeignKey(help_text='Select item to return from customer', on_delete=django.db.models.deletion.CASCADE, related_name='return_order_lines', to='stock.stockitem', verbose_name='Item')), + ('order', models.ForeignKey(help_text='Return Order', on_delete=django.db.models.deletion.CASCADE, related_name='lines', to='order.returnorder', verbose_name='Order')), + ('metadata', models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata')), + ('outcome_custom_key', generic.states.fields.ExtraInvenTreeCustomStatusModelField(blank=True, default=None, help_text='Additional status information for this item', null=True, validators=[generic.states.validators.CustomStatusCodeValidator(status_class=order.status_codes.ReturnOrderLineStatus)], verbose_name='Custom status key')), + ], + options={ + 'unique_together': {('order', 'item')}, + 'verbose_name': 'Return Order Line Item', + }, + ), + migrations.AlterField( + model_name='salesorder', + name='total_price_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='salesorderextraline', + name='price_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='salesorderlineitem', + name='sale_price_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='salesorder', + name='status', + field=generic.states.fields.InvenTreeCustomStatusModelField(choices=[(10, 'Pending'), (15, 'In Progress'), (20, 'Shipped'), (25, 'On Hold'), (30, 'Complete'), (40, 'Cancelled'), (50, 'Lost'), (60, 'Returned')], default=10, help_text='Sales order status', validators=[generic.states.validators.CustomStatusCodeValidator(status_class=order.status_codes.SalesOrderStatus)], verbose_name='Status'), + ), + migrations.DeleteModel( + name='PurchaseOrderAttachment', + ), + migrations.DeleteModel( + name='ReturnOrderAttachment', + ), + migrations.DeleteModel( + name='SalesOrderAttachment', + ), + migrations.AddField( + model_name='purchaseorder', + name='status_custom_key', + field=generic.states.fields.ExtraInvenTreeCustomStatusModelField(blank=True, default=None, help_text='Additional status information for this item', null=True, validators=[generic.states.validators.CustomStatusCodeValidator(status_class=order.status_codes.PurchaseOrderStatus)], verbose_name='Custom status key'), + ), + migrations.AddField( + model_name='salesorder', + name='status_custom_key', + field=generic.states.fields.ExtraInvenTreeCustomStatusModelField(blank=True, default=None, help_text='Additional status information for this item', null=True, validators=[generic.states.validators.CustomStatusCodeValidator(status_class=order.status_codes.SalesOrderStatus)], verbose_name='Custom status key'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='status', + field=generic.states.fields.InvenTreeCustomStatusModelField(choices=[(10, 'Pending'), (20, 'Placed'), (25, 'On Hold'), (30, 'Complete'), (40, 'Cancelled'), (50, 'Lost'), (60, 'Returned')], default=10, help_text='Purchase order status', validators=[generic.states.validators.CustomStatusCodeValidator(status_class=order.status_codes.PurchaseOrderStatus)], verbose_name='Status'), + ), + migrations.AddField( + model_name='purchaseorder', + name='destination', + field=mptt.fields.TreeForeignKey(blank=True, help_text='Destination for received items', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='purchase_orders', to='stock.stocklocation', verbose_name='Destination'), + ), + migrations.AlterField( + model_name='purchaseorderlineitem', + name='destination', + field=mptt.fields.TreeForeignKey(blank=True, help_text='Destination for received items', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='po_lines', to='stock.stocklocation', verbose_name='Destination'), + ), + migrations.AlterField( + model_name='salesorderallocation', + name='shipment', + field=models.ForeignKey(blank=True, help_text='Sales order shipment reference', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='order.salesordershipment', verbose_name='Shipment'), + ), + migrations.AddField( + model_name='purchaseorder', + name='start_date', + field=models.DateField(blank=True, help_text='Scheduled start date for this order', null=True, verbose_name='Start date'), + ), + migrations.AddField( + model_name='returnorder', + name='start_date', + field=models.DateField(blank=True, help_text='Scheduled start date for this order', null=True, verbose_name='Start date'), + ), + migrations.AddField( + model_name='salesorder', + name='start_date', + field=models.DateField(blank=True, help_text='Scheduled start date for this order', null=True, verbose_name='Start date'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link'), + ), + migrations.AlterField( + model_name='purchaseorderlineitem', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link'), + ), + migrations.AlterField( + model_name='returnorder', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link'), + ), + migrations.AlterField( + model_name='returnorderlineitem', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link'), + ), + migrations.AlterField( + model_name='salesorder', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link'), + ), + migrations.AlterField( + model_name='salesorderlineitem', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link'), + ), + migrations.AlterField( + model_name='salesordershipment', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link'), + ), + migrations.AlterField( + model_name='purchaseorderextraline', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link'), + ), + migrations.AlterField( + model_name='returnorderextraline', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link'), + ), + migrations.AlterField( + model_name='salesorderextraline', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', max_length=2000, verbose_name='Link'), + ), + migrations.AddField( + model_name='salesorder', + name='issue_date', + field=models.DateField(blank=True, help_text='Date order was issued', null=True, verbose_name='Issue Date'), + ), + migrations.AddField( + model_name='salesordershipment', + name='barcode_data', + field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), + ), + migrations.AddField( + model_name='salesordershipment', + name='barcode_hash', + field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), + ), + migrations.AddField( + model_name='purchaseorderlineitem', + name='build_order', + field=models.ForeignKey(blank=True, help_text='External Build Order to be fulfilled by this line item', limit_choices_to={'external': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='external_line_items', to='build.build', verbose_name='Build Order'), + ), + migrations.AlterField( + model_name='salesorderlineitem', + name='part', + field=models.ForeignKey(help_text='Part', limit_choices_to={'salable': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sales_order_line_items', to='part.part', verbose_name='Part'), + ), + ] diff --git a/src/backend/InvenTree/order/migrations/0032_auto_20200427_0044.py b/src/backend/InvenTree/order/migrations/0032_auto_20200427_0044.py deleted file mode 100644 index 4648ca911b..0000000000 --- a/src/backend/InvenTree/order/migrations/0032_auto_20200427_0044.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.5 on 2020-04-27 00:44 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0035_auto_20200406_0045'), - ('order', '0031_auto_20200426_0612'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='salesorderlineitem', - unique_together={('order', 'part')}, - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0033_auto_20200512_1033.py b/src/backend/InvenTree/order/migrations/0033_auto_20200512_1033.py deleted file mode 100644 index 2c3abbb0d0..0000000000 --- a/src/backend/InvenTree/order/migrations/0033_auto_20200512_1033.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 3.0.5 on 2020-05-12 10:33 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('order', '0032_auto_20200427_0044'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorderattachment', - name='user', - field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='salesorderattachment', - name='user', - field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0034_auto_20200512_1054.py b/src/backend/InvenTree/order/migrations/0034_auto_20200512_1054.py deleted file mode 100644 index 9124bb1cce..0000000000 --- a/src/backend/InvenTree/order/migrations/0034_auto_20200512_1054.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.0.5 on 2020-05-12 10:54 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0033_auto_20200512_1033'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorderattachment', - name='upload_date', - field=models.DateField(auto_now_add=True, null=True), - ), - migrations.AddField( - model_name='salesorderattachment', - name='upload_date', - field=models.DateField(auto_now_add=True, null=True), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0035_auto_20200513_0016.py b/src/backend/InvenTree/order/migrations/0035_auto_20200513_0016.py deleted file mode 100644 index 101a55764e..0000000000 --- a/src/backend/InvenTree/order/migrations/0035_auto_20200513_0016.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.0.5 on 2020-05-13 00:16 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0034_auto_20200512_1054'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorderattachment', - name='comment', - field=models.CharField(blank=True, help_text='File comment', max_length=100), - ), - migrations.AlterField( - model_name='salesorderattachment', - name='comment', - field=models.CharField(blank=True, help_text='File comment', max_length=100), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0036_auto_20200831_0912.py b/src/backend/InvenTree/order/migrations/0036_auto_20200831_0912.py deleted file mode 100644 index 1329d0a8d5..0000000000 --- a/src/backend/InvenTree/order/migrations/0036_auto_20200831_0912.py +++ /dev/null @@ -1,35 +0,0 @@ -# Generated by Django 3.0.7 on 2020-08-31 09:12 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0023_auto_20200808_0715'), - ('order', '0035_auto_20200513_0016'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorder', - name='complete_date', - field=models.DateField(blank=True, help_text='Date order was completed', null=True), - ), - migrations.AlterField( - model_name='purchaseorder', - name='issue_date', - field=models.DateField(blank=True, help_text='Date order was issued', null=True), - ), - migrations.AlterField( - model_name='purchaseorder', - name='supplier', - field=models.ForeignKey(help_text='Company from which the items are being ordered', limit_choices_to={'is_supplier': True}, on_delete=django.db.models.deletion.CASCADE, related_name='purchase_orders', to='company.Company'), - ), - migrations.AlterField( - model_name='salesorder', - name='customer', - field=models.ForeignKey(help_text='Company to which the items are being sold', limit_choices_to={'is_customer': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sales_orders', to='company.Company'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0037_auto_20201110_0911.py b/src/backend/InvenTree/order/migrations/0037_auto_20201110_0911.py deleted file mode 100644 index 9594424e5d..0000000000 --- a/src/backend/InvenTree/order/migrations/0037_auto_20201110_0911.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-10 09:11 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0054_remove_stockitem_build_order'), - ('order', '0036_auto_20200831_0912'), - ] - - operations = [ - migrations.AlterField( - model_name='salesorderallocation', - name='item', - field=models.ForeignKey(help_text='Select stock item to allocate', limit_choices_to={'belongs_to': None, 'part__salable': True, 'sales_order': None}, on_delete=django.db.models.deletion.CASCADE, related_name='sales_order_allocations', to='stock.StockItem'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0038_auto_20201112_1737.py b/src/backend/InvenTree/order/migrations/0038_auto_20201112_1737.py deleted file mode 100644 index e4014a00ed..0000000000 --- a/src/backend/InvenTree/order/migrations/0038_auto_20201112_1737.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-12 06:37 - -from django.db import migrations -import djmoney.models.fields -import common.currency - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0004_inventreesetting'), - ('order', '0037_auto_20201110_0911'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorderlineitem', - name='purchase_price', - field=djmoney.models.fields.MoneyField(decimal_places=4, default_currency='', help_text='Unit purchase price', max_digits=19, null=True, verbose_name='Purchase Price'), - ), - migrations.AddField( - model_name='purchaseorderlineitem', - name='purchase_price_currency', - field=djmoney.models.fields.CurrencyField(choices=common.currency.currency_code_mappings(), default='', editable=False, max_length=3), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0039_auto_20201112_2203.py b/src/backend/InvenTree/order/migrations/0039_auto_20201112_2203.py deleted file mode 100644 index d9af677fa0..0000000000 --- a/src/backend/InvenTree/order/migrations/0039_auto_20201112_2203.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-12 11:03 - -from django.db import migrations -import djmoney.models.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0038_auto_20201112_1737'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorderlineitem', - name='purchase_price', - field=djmoney.models.fields.MoneyField(blank=True, decimal_places=4, default_currency='', help_text='Unit purchase price', max_digits=19, null=True, verbose_name='Purchase Price'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0040_salesorder_target_date.py b/src/backend/InvenTree/order/migrations/0040_salesorder_target_date.py deleted file mode 100644 index 29a90a6ad5..0000000000 --- a/src/backend/InvenTree/order/migrations/0040_salesorder_target_date.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.7 on 2020-12-18 01:16 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0039_auto_20201112_2203'), - ] - - operations = [ - migrations.AddField( - model_name='salesorder', - name='target_date', - field=models.DateField(blank=True, help_text='Target date for order completion. Order will be overdue after this date.', null=True, verbose_name='Target completion date'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0041_auto_20210114_1728.py b/src/backend/InvenTree/order/migrations/0041_auto_20210114_1728.py deleted file mode 100644 index 09be471433..0000000000 --- a/src/backend/InvenTree/order/migrations/0041_auto_20210114_1728.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 3.0.7 on 2021-01-14 06:28 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0040_salesorder_target_date'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorder', - name='target_date', - field=models.DateField(blank=True, help_text='Expected date for order delivery. Order will be overdue after this date.', null=True, verbose_name='Target Delivery Date'), - ), - migrations.AlterField( - model_name='purchaseorder', - name='complete_date', - field=models.DateField(blank=True, help_text='Date order was completed', null=True, verbose_name='Completion Date'), - ), - migrations.AlterField( - model_name='purchaseorder', - name='issue_date', - field=models.DateField(blank=True, help_text='Date order was issued', null=True, verbose_name='Issue Date'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0042_auto_20210310_1619.py b/src/backend/InvenTree/order/migrations/0042_auto_20210310_1619.py deleted file mode 100644 index 9d07bc18f3..0000000000 --- a/src/backend/InvenTree/order/migrations/0042_auto_20210310_1619.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 3.0.7 on 2021-03-10 05:19 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0005_owner_model'), - ('order', '0041_auto_20210114_1728'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorder', - name='responsible', - field=models.ForeignKey(blank=True, help_text='User or group responsible for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='users.Owner', verbose_name='Responsible'), - ), - migrations.AddField( - model_name='salesorder', - name='responsible', - field=models.ForeignKey(blank=True, help_text='User or group responsible for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='users.Owner', verbose_name='Responsible'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0043_auto_20210330_0013.py b/src/backend/InvenTree/order/migrations/0043_auto_20210330_0013.py deleted file mode 100644 index 35c4b99bcb..0000000000 --- a/src/backend/InvenTree/order/migrations/0043_auto_20210330_0013.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.0.7 on 2021-03-29 13:13 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0042_auto_20210310_1619'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='salesorderlineitem', - unique_together=set(), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0044_auto_20210404_2016.py b/src/backend/InvenTree/order/migrations/0044_auto_20210404_2016.py deleted file mode 100644 index d97c9accc1..0000000000 --- a/src/backend/InvenTree/order/migrations/0044_auto_20210404_2016.py +++ /dev/null @@ -1,232 +0,0 @@ -# Generated by Django 3.0.7 on 2021-04-04 20:16 - -import InvenTree.fields -import InvenTree.models -from django.conf import settings -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0032_auto_20210403_1837'), - ('part', '0063_bomitem_inherited'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('stock', '0058_stockitem_packaging'), - ('order', '0043_auto_20210330_0013'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorder', - name='created_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='purchaseorder', - name='creation_date', - field=models.DateField(blank=True, null=True, verbose_name='Creation Date'), - ), - migrations.AlterField( - model_name='purchaseorder', - name='description', - field=models.CharField(help_text='Order description', max_length=250, verbose_name='Description'), - ), - migrations.AlterField( - model_name='purchaseorder', - name='link', - field=models.URLField(blank=True, help_text='Link to external page', verbose_name='Link'), - ), - migrations.AlterField( - model_name='purchaseorder', - name='notes', - field=models.TextField(blank=True, help_text='Order notes', verbose_name='Notes'), - ), - migrations.AlterField( - model_name='purchaseorder', - name='received_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='received by'), - ), - migrations.AlterField( - model_name='purchaseorder', - name='reference', - field=models.CharField(help_text='Order reference', max_length=64, unique=True, verbose_name='Reference'), - ), - migrations.AlterField( - model_name='purchaseorder', - name='supplier', - field=models.ForeignKey(help_text='Company from which the items are being ordered', limit_choices_to={'is_supplier': True}, on_delete=django.db.models.deletion.CASCADE, related_name='purchase_orders', to='company.Company', verbose_name='Supplier'), - ), - migrations.AlterField( - model_name='purchaseorder', - name='supplier_reference', - field=models.CharField(blank=True, help_text='Supplier order reference code', max_length=64, verbose_name='Supplier Reference'), - ), - migrations.AlterField( - model_name='purchaseorderattachment', - name='attachment', - field=models.FileField(help_text='Select file to attach', upload_to='attachments', verbose_name='Attachment'), - ), - migrations.AlterField( - model_name='purchaseorderattachment', - name='comment', - field=models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment'), - ), - migrations.AlterField( - model_name='purchaseorderattachment', - name='upload_date', - field=models.DateField(auto_now_add=True, null=True, verbose_name='upload date'), - ), - migrations.AlterField( - model_name='purchaseorderattachment', - name='user', - field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User'), - ), - migrations.AlterField( - model_name='purchaseorderlineitem', - name='notes', - field=models.CharField(blank=True, help_text='Line item notes', max_length=500, verbose_name='Notes'), - ), - migrations.AlterField( - model_name='purchaseorderlineitem', - name='order', - field=models.ForeignKey(help_text='Purchase Order', on_delete=django.db.models.deletion.CASCADE, related_name='lines', to='order.PurchaseOrder', verbose_name='Order'), - ), - migrations.AlterField( - model_name='purchaseorderlineitem', - name='part', - field=models.ForeignKey(blank=True, help_text='Supplier part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='purchase_order_line_items', to='company.SupplierPart', verbose_name='Part'), - ), - migrations.AlterField( - model_name='purchaseorderlineitem', - name='quantity', - field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Item quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'), - ), - migrations.AlterField( - model_name='purchaseorderlineitem', - name='received', - field=models.DecimalField(decimal_places=5, default=0, help_text='Number of items received', max_digits=15, verbose_name='Received'), - ), - migrations.AlterField( - model_name='purchaseorderlineitem', - name='reference', - field=models.CharField(blank=True, help_text='Line item reference', max_length=100, verbose_name='Reference'), - ), - migrations.AlterField( - model_name='salesorder', - name='created_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Created By'), - ), - migrations.AlterField( - model_name='salesorder', - name='creation_date', - field=models.DateField(blank=True, null=True, verbose_name='Creation Date'), - ), - migrations.AlterField( - model_name='salesorder', - name='customer', - field=models.ForeignKey(help_text='Company to which the items are being sold', limit_choices_to={'is_customer': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sales_orders', to='company.Company', verbose_name='Customer'), - ), - migrations.AlterField( - model_name='salesorder', - name='customer_reference', - field=models.CharField(blank=True, help_text='Customer order reference code', max_length=64, verbose_name='Customer Reference '), - ), - migrations.AlterField( - model_name='salesorder', - name='description', - field=models.CharField(help_text='Order description', max_length=250, verbose_name='Description'), - ), - migrations.AlterField( - model_name='salesorder', - name='link', - field=models.URLField(blank=True, help_text='Link to external page', verbose_name='Link'), - ), - migrations.AlterField( - model_name='salesorder', - name='notes', - field=models.TextField(blank=True, help_text='Order notes', verbose_name='Notes'), - ), - migrations.AlterField( - model_name='salesorder', - name='reference', - field=models.CharField(help_text='Order reference', max_length=64, unique=True, verbose_name='Reference'), - ), - migrations.AlterField( - model_name='salesorder', - name='shipment_date', - field=models.DateField(blank=True, null=True, verbose_name='Shipment Date'), - ), - migrations.AlterField( - model_name='salesorder', - name='shipped_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='shipped by'), - ), - migrations.AlterField( - model_name='salesorder', - name='status', - field=models.PositiveIntegerField(choices=[(10, 'Pending'), (20, 'Shipped'), (40, 'Cancelled'), (50, 'Lost'), (60, 'Returned')], default=10, help_text='Purchase order status', verbose_name='Status'), - ), - migrations.AlterField( - model_name='salesorderallocation', - name='item', - field=models.ForeignKey(help_text='Select stock item to allocate', limit_choices_to={'belongs_to': None, 'part__salable': True, 'sales_order': None}, on_delete=django.db.models.deletion.CASCADE, related_name='sales_order_allocations', to='stock.StockItem', verbose_name='Item'), - ), - migrations.AlterField( - model_name='salesorderallocation', - name='line', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='order.SalesOrderLineItem', verbose_name='Line'), - ), - migrations.AlterField( - model_name='salesorderallocation', - name='quantity', - field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Enter stock allocation quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'), - ), - migrations.AlterField( - model_name='salesorderattachment', - name='attachment', - field=models.FileField(help_text='Select file to attach', upload_to='attachments', verbose_name='Attachment'), - ), - migrations.AlterField( - model_name='salesorderattachment', - name='comment', - field=models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment'), - ), - migrations.AlterField( - model_name='salesorderattachment', - name='upload_date', - field=models.DateField(auto_now_add=True, null=True, verbose_name='upload date'), - ), - migrations.AlterField( - model_name='salesorderattachment', - name='user', - field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User'), - ), - migrations.AlterField( - model_name='salesorderlineitem', - name='notes', - field=models.CharField(blank=True, help_text='Line item notes', max_length=500, verbose_name='Notes'), - ), - migrations.AlterField( - model_name='salesorderlineitem', - name='order', - field=models.ForeignKey(help_text='Sales Order', on_delete=django.db.models.deletion.CASCADE, related_name='lines', to='order.SalesOrder', verbose_name='Order'), - ), - migrations.AlterField( - model_name='salesorderlineitem', - name='part', - field=models.ForeignKey(help_text='Part', limit_choices_to={'salable': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sales_order_line_items', to='part.Part', verbose_name='Part'), - ), - migrations.AlterField( - model_name='salesorderlineitem', - name='quantity', - field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Item quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'), - ), - migrations.AlterField( - model_name='salesorderlineitem', - name='reference', - field=models.CharField(blank=True, help_text='Line item reference', max_length=100, verbose_name='Reference'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0045_auto_20210504_1946.py b/src/backend/InvenTree/order/migrations/0045_auto_20210504_1946.py deleted file mode 100644 index 4366e6a4cb..0000000000 --- a/src/backend/InvenTree/order/migrations/0045_auto_20210504_1946.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 3.2 on 2021-05-04 19:46 - -from django.db import migrations -import common.currency -import djmoney.models.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0044_auto_20210404_2016'), - ] - - operations = [ - migrations.AddField( - model_name='salesorderlineitem', - name='sale_price', - field=djmoney.models.fields.MoneyField(blank=True, decimal_places=4, default_currency='', help_text='Unit sale price', max_digits=19, null=True, verbose_name='Sale Price'), - ), - migrations.AddField( - model_name='salesorderlineitem', - name='sale_price_currency', - field=djmoney.models.fields.CurrencyField(choices=common.currency.currency_code_mappings(), default='', editable=False, max_length=3), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0046_purchaseorderlineitem_destination.py b/src/backend/InvenTree/order/migrations/0046_purchaseorderlineitem_destination.py deleted file mode 100644 index fa08c91e0d..0000000000 --- a/src/backend/InvenTree/order/migrations/0046_purchaseorderlineitem_destination.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 3.2 on 2021-05-13 22:38 - -from django.db import migrations -import django.db.models.deletion -import mptt.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ("stock", "0063_auto_20210511_2343"), - ("order", "0045_auto_20210504_1946"), - ] - - operations = [ - migrations.AddField( - model_name="purchaseorderlineitem", - name="destination", - field=mptt.fields.TreeForeignKey( - blank=True, - help_text="Where does the Purchaser want this item to be stored?", - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="po_lines", - to="stock.stocklocation", - verbose_name="Destination", - ), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0047_auto_20210701_0509.py b/src/backend/InvenTree/order/migrations/0047_auto_20210701_0509.py deleted file mode 100644 index 1f732e5f61..0000000000 --- a/src/backend/InvenTree/order/migrations/0047_auto_20210701_0509.py +++ /dev/null @@ -1,35 +0,0 @@ -# Generated by Django 3.2.4 on 2021-07-01 05:09 - -import InvenTree.fields -from django.db import migrations -import djmoney.models.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0046_purchaseorderlineitem_destination'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorderlineitem', - name='purchase_price', - field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=4, default_currency='', help_text='Unit purchase price', max_digits=19, null=True, verbose_name='Purchase Price'), - ), - migrations.AlterField( - model_name='purchaseorderlineitem', - name='purchase_price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3), - ), - migrations.AlterField( - model_name='salesorderlineitem', - name='sale_price', - field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=4, default_currency='', help_text='Unit sale price', max_digits=19, null=True, verbose_name='Sale Price'), - ), - migrations.AlterField( - model_name='salesorderlineitem', - name='sale_price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0048_auto_20210702_2321.py b/src/backend/InvenTree/order/migrations/0048_auto_20210702_2321.py deleted file mode 100644 index 6d68c1a0ca..0000000000 --- a/src/backend/InvenTree/order/migrations/0048_auto_20210702_2321.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.4 on 2021-07-02 13:21 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0047_auto_20210701_0509'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorder', - name='reference', - field=models.CharField(default="PO", help_text='Order reference', max_length=64, unique=True, verbose_name='Reference'), - ), - migrations.AlterField( - model_name='salesorder', - name='reference', - field=models.CharField(default="SO", help_text='Order reference', max_length=64, unique=True, verbose_name='Reference'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0049_alter_purchaseorderlineitem_unique_together.py b/src/backend/InvenTree/order/migrations/0049_alter_purchaseorderlineitem_unique_together.py deleted file mode 100644 index c451e1754d..0000000000 --- a/src/backend/InvenTree/order/migrations/0049_alter_purchaseorderlineitem_unique_together.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.4 on 2021-08-12 17:49 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0040_alter_company_currency'), - ('order', '0048_auto_20210702_2321'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='purchaseorderlineitem', - unique_together={('order', 'part', 'quantity', 'purchase_price')}, - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0050_alter_purchaseorderlineitem_destination.py b/src/backend/InvenTree/order/migrations/0050_alter_purchaseorderlineitem_destination.py deleted file mode 100644 index 7c0620d053..0000000000 --- a/src/backend/InvenTree/order/migrations/0050_alter_purchaseorderlineitem_destination.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.2.4 on 2021-09-02 00:42 - -from django.db import migrations -import django.db.models.deletion -import mptt.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0065_auto_20210701_0509'), - ('order', '0049_alter_purchaseorderlineitem_unique_together'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorderlineitem', - name='destination', - field=mptt.fields.TreeForeignKey(blank=True, help_text='Where does the Purchaser want this item to be stored?', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='po_lines', to='stock.stocklocation', verbose_name='Destination'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0051_auto_20211014_0623.py b/src/backend/InvenTree/order/migrations/0051_auto_20211014_0623.py deleted file mode 100644 index 20cc893dd2..0000000000 --- a/src/backend/InvenTree/order/migrations/0051_auto_20211014_0623.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.5 on 2021-10-14 06:23 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0050_alter_purchaseorderlineitem_destination'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorder', - name='reference_int', - field=models.IntegerField(default=0), - ), - migrations.AddField( - model_name='salesorder', - name='reference_int', - field=models.IntegerField(default=0), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0052_auto_20211014_0631.py b/src/backend/InvenTree/order/migrations/0052_auto_20211014_0631.py deleted file mode 100644 index 9f6589bc3f..0000000000 --- a/src/backend/InvenTree/order/migrations/0052_auto_20211014_0631.py +++ /dev/null @@ -1,64 +0,0 @@ -# Generated by Django 3.2.5 on 2021-10-14 06:31 - -import re - -from django.db import migrations - -def build_refs(apps, schema_editor): - """ - Rebuild the integer "reference fields" for existing Build objects - """ - - PurchaseOrder = apps.get_model('order', 'purchaseorder') - - for order in PurchaseOrder.objects.all(): - - ref = 0 - - result = re.match(r"^(\d+)", order.reference) - - if result and len(result.groups()) == 1: - try: - ref = int(result.groups()[0]) - except Exception: # pragma: no cover - ref = 0 - - # Clip integer value to ensure it does not overflow database field - if ref > 0x7fffffff: - ref = 0x7fffffff - - order.reference_int = ref - order.save() - - SalesOrder = apps.get_model('order', 'salesorder') - - for order in SalesOrder.objects.all(): - - ref = 0 - - result = re.match(r"^(\d+)", order.reference) - - if result and len(result.groups()) == 1: - try: - ref = int(result.groups()[0]) - except Exception: # pragma: no cover - ref = 0 - - # Clip integer value to ensure it does not overflow database field - if ref > 0x7fffffff: - ref = 0x7fffffff - - order.reference_int = ref - order.save() - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0051_auto_20211014_0623'), - ] - - - operations = [ - migrations.RunPython(build_refs, reverse_code=migrations.RunPython.noop) - ] diff --git a/src/backend/InvenTree/order/migrations/0053_auto_20211128_0151.py b/src/backend/InvenTree/order/migrations/0053_auto_20211128_0151.py deleted file mode 100644 index a4e0ec30a0..0000000000 --- a/src/backend/InvenTree/order/migrations/0053_auto_20211128_0151.py +++ /dev/null @@ -1,35 +0,0 @@ -# Generated by Django 3.2.5 on 2021-11-28 01:51 - -import InvenTree.fields -import InvenTree.models -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0052_auto_20211014_0631'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorderattachment', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', null=True, verbose_name='Link'), - ), - migrations.AddField( - model_name='salesorderattachment', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', null=True, verbose_name='Link'), - ), - migrations.AlterField( - model_name='purchaseorderattachment', - name='attachment', - field=models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to='attachments', verbose_name='Attachment'), - ), - migrations.AlterField( - model_name='salesorderattachment', - name='attachment', - field=models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to='attachments', verbose_name='Attachment'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0053_salesordershipment.py b/src/backend/InvenTree/order/migrations/0053_salesordershipment.py deleted file mode 100644 index 440b9fbeef..0000000000 --- a/src/backend/InvenTree/order/migrations/0053_salesordershipment.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 3.2.5 on 2021-10-25 02:08 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('order', '0053_auto_20211128_0151'), - ] - - operations = [ - migrations.CreateModel( - name='SalesOrderShipment', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('shipment_date', models.DateField(blank=True, help_text='Date of shipment', null=True, verbose_name='Shipment Date')), - ('reference', models.CharField(default='1', help_text='Shipment reference', max_length=100, verbose_name='Reference')), - ('notes', models.TextField(blank=True, help_text='Shipment notes', verbose_name='Notes')), - ('checked_by', models.ForeignKey(blank=True, help_text='User who checked this shipment', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Checked By')), - ('order', models.ForeignKey(help_text='Sales Order', on_delete=django.db.models.deletion.CASCADE, related_name='shipments', to='order.salesorder', verbose_name='Order')), - ], - options={ - 'verbose_name': 'Sales Order Shipment', - }, - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0054_auto_20211201_2139.py b/src/backend/InvenTree/order/migrations/0054_auto_20211201_2139.py deleted file mode 100644 index 8a66bbea12..0000000000 --- a/src/backend/InvenTree/order/migrations/0054_auto_20211201_2139.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.5 on 2021-12-01 21:39 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0053_auto_20211128_0151'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorder', - name='reference_int', - field=models.BigIntegerField(default=0), - ), - migrations.AlterField( - model_name='salesorder', - name='reference_int', - field=models.BigIntegerField(default=0), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0054_salesorderallocation_shipment.py b/src/backend/InvenTree/order/migrations/0054_salesorderallocation_shipment.py deleted file mode 100644 index cfb74dd859..0000000000 --- a/src/backend/InvenTree/order/migrations/0054_salesorderallocation_shipment.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.5 on 2021-10-25 06:42 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0053_salesordershipment'), - ] - - operations = [ - migrations.AddField( - model_name='salesorderallocation', - name='shipment', - field=models.ForeignKey(blank=True, help_text='Sales order shipment reference', null=True, on_delete=django.db.models.deletion.CASCADE, to='order.salesordershipment', verbose_name='Shipment'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0055_auto_20211025_0645.py b/src/backend/InvenTree/order/migrations/0055_auto_20211025_0645.py deleted file mode 100644 index 69ef6e6add..0000000000 --- a/src/backend/InvenTree/order/migrations/0055_auto_20211025_0645.py +++ /dev/null @@ -1,92 +0,0 @@ -# Generated by Django 3.2.5 on 2021-10-25 06:45 - -from django.db import migrations - - -from order.status_codes import SalesOrderStatus - - -def add_shipment(apps, schema_editor): - """ - Create a SalesOrderShipment for each existing SalesOrder instance. - - Any "allocations" are marked against that shipment. - - For each existing SalesOrder instance, we create a default SalesOrderShipment, - and associate each SalesOrderAllocation with this shipment - """ - - Allocation = apps.get_model('order', 'salesorderallocation') - SalesOrder = apps.get_model('order', 'salesorder') - Shipment = apps.get_model('order', 'salesordershipment') - - n = 0 - - for order in SalesOrder.objects.all(): - - """ - We only create an automatic shipment for "PENDING" orders, - as SalesOrderAllocations were historically deleted for "SHIPPED" or "CANCELLED" orders - """ - - allocations = Allocation.objects.filter( - line__order=order - ) - - if allocations.count() == 0 and order.status != SalesOrderStatus.PENDING: # pragma: no cover - continue - - # Create a new Shipment instance against this order - shipment = Shipment.objects.create( - order=order, - ) - - if order.status == SalesOrderStatus.SHIPPED: # pragma: no cover - shipment.shipment_date = order.shipment_date - - shipment.save() - - # Iterate through each allocation associated with this order - for allocation in allocations: # pragma: no cover - allocation.shipment = shipment - allocation.save() - - n += 1 - - if n > 0: - print(f"\nCreated SalesOrderShipment for {n} SalesOrder instances") - - -def reverse_add_shipment(apps, schema_editor): # pragma: no cover - """ - Reverse the migration, delete and SalesOrderShipment instances - """ - - Allocation = apps.get_model('order', 'salesorderallocation') - - # First, ensure that all SalesOrderAllocation objects point to a null shipment - for allocation in Allocation.objects.exclude(shipment=None): - allocation.shipment = None - allocation.save() - - SOS = apps.get_model('order', 'salesordershipment') - - n = SOS.objects.count() - - print(f"Deleting {n} SalesOrderShipment instances") - - SOS.objects.all().delete() - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0054_salesorderallocation_shipment'), - ] - - operations = [ - migrations.RunPython( - add_shipment, - reverse_code=reverse_add_shipment, - ) - ] diff --git a/src/backend/InvenTree/order/migrations/0056_alter_salesorderallocation_shipment.py b/src/backend/InvenTree/order/migrations/0056_alter_salesorderallocation_shipment.py deleted file mode 100644 index 340a8d4ab5..0000000000 --- a/src/backend/InvenTree/order/migrations/0056_alter_salesorderallocation_shipment.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.5 on 2021-10-25 11:40 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0055_auto_20211025_0645'), - ] - - operations = [ - migrations.AlterField( - model_name='salesorderallocation', - name='shipment', - field=models.ForeignKey(help_text='Sales order shipment reference', on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='order.salesordershipment', verbose_name='Shipment'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0057_salesorderlineitem_shipped.py b/src/backend/InvenTree/order/migrations/0057_salesorderlineitem_shipped.py deleted file mode 100644 index e11eb62cbb..0000000000 --- a/src/backend/InvenTree/order/migrations/0057_salesorderlineitem_shipped.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.5 on 2021-11-26 12:06 - -import InvenTree.fields -import django.core.validators -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0056_alter_salesorderallocation_shipment'), - ] - - operations = [ - migrations.AddField( - model_name='salesorderlineitem', - name='shipped', - field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=0, help_text='Shipped quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Shipped'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0058_auto_20211126_1210.py b/src/backend/InvenTree/order/migrations/0058_auto_20211126_1210.py deleted file mode 100644 index 6c31d93099..0000000000 --- a/src/backend/InvenTree/order/migrations/0058_auto_20211126_1210.py +++ /dev/null @@ -1,54 +0,0 @@ -# Generated by Django 3.2.5 on 2021-11-26 12:10 - -from django.db import migrations - -from order.status_codes import SalesOrderStatus - - -def calculate_shipped_quantity(apps, schema_editor): - """ - In migration 0057 we added a new field 'shipped' to the SalesOrderLineItem model. - - This field is used to record the number of items shipped, - even if the actual stock items get deleted from the database. - - For existing orders in the database, we calculate this as follows: - - - If the order is "shipped" then we use the total quantity - - Otherwise, we use the "fulfilled" calculated quantity - - """ - - StockItem = apps.get_model('stock', 'stockitem') - SalesOrderLineItem = apps.get_model('order', 'salesorderlineitem') - - for item in SalesOrderLineItem.objects.all(): # pragma: no cover - - if item.order.status == SalesOrderStatus.SHIPPED: - item.shipped = item.quantity - else: - # Calculate total stock quantity of items allocated to this order? - items = StockItem.objects.filter( - sales_order=item.order, - part=item.part - ) - - q = sum(item.quantity for item in items) - - item.shipped = q - - item.save() - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0057_salesorderlineitem_shipped'), - ] - - operations = [ - migrations.RunPython( - calculate_shipped_quantity, - reverse_code=migrations.RunPython.noop - ) - ] diff --git a/src/backend/InvenTree/order/migrations/0059_salesordershipment_tracking_number.py b/src/backend/InvenTree/order/migrations/0059_salesordershipment_tracking_number.py deleted file mode 100644 index 473761fb4a..0000000000 --- a/src/backend/InvenTree/order/migrations/0059_salesordershipment_tracking_number.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.5 on 2021-11-29 11:58 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0058_auto_20211126_1210'), - ] - - operations = [ - migrations.AddField( - model_name='salesordershipment', - name='tracking_number', - field=models.CharField(blank=True, help_text='Shipment tracking information', max_length=100, verbose_name='Tracking Number'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0060_auto_20211129_1339.py b/src/backend/InvenTree/order/migrations/0060_auto_20211129_1339.py deleted file mode 100644 index 2166ec45ad..0000000000 --- a/src/backend/InvenTree/order/migrations/0060_auto_20211129_1339.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 3.2.5 on 2021-11-29 13:39 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0059_salesordershipment_tracking_number'), - ] - - operations = [ - migrations.AlterField( - model_name='salesordershipment', - name='reference', - field=models.CharField(default='1', help_text='Shipment number', max_length=100, verbose_name='Shipment'), - ), - migrations.AlterUniqueTogether( - name='salesordershipment', - unique_together={('order', 'reference')}, - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0061_merge_0054_auto_20211201_2139_0060_auto_20211129_1339.py b/src/backend/InvenTree/order/migrations/0061_merge_0054_auto_20211201_2139_0060_auto_20211129_1339.py deleted file mode 100644 index e844b0fc23..0000000000 --- a/src/backend/InvenTree/order/migrations/0061_merge_0054_auto_20211201_2139_0060_auto_20211129_1339.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 3.2.5 on 2021-12-02 13:31 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0054_auto_20211201_2139'), - ('order', '0060_auto_20211129_1339'), - ] - - operations = [ - ] diff --git a/src/backend/InvenTree/order/migrations/0062_auto_20220228_0321.py b/src/backend/InvenTree/order/migrations/0062_auto_20220228_0321.py deleted file mode 100644 index 7f67a827a2..0000000000 --- a/src/backend/InvenTree/order/migrations/0062_auto_20220228_0321.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.10 on 2022-02-28 03:21 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0061_merge_0054_auto_20211201_2139_0060_auto_20211129_1339'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorderlineitem', - name='target_date', - field=models.DateField(blank=True, help_text='Target shipping date for this line item', null=True, verbose_name='Target Date'), - ), - migrations.AddField( - model_name='salesorderlineitem', - name='target_date', - field=models.DateField(blank=True, help_text='Target shipping date for this line item', null=True, verbose_name='Target Date'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0063_alter_purchaseorderlineitem_unique_together.py b/src/backend/InvenTree/order/migrations/0063_alter_purchaseorderlineitem_unique_together.py deleted file mode 100644 index 1c73b6b437..0000000000 --- a/src/backend/InvenTree/order/migrations/0063_alter_purchaseorderlineitem_unique_together.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.2.10 on 2022-02-28 04:27 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0062_auto_20220228_0321'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='purchaseorderlineitem', - unique_together=set(), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0064_purchaseorderextraline_salesorderextraline.py b/src/backend/InvenTree/order/migrations/0064_purchaseorderextraline_salesorderextraline.py deleted file mode 100644 index 86784edf93..0000000000 --- a/src/backend/InvenTree/order/migrations/0064_purchaseorderextraline_salesorderextraline.py +++ /dev/null @@ -1,111 +0,0 @@ -# Generated by Django 3.2.12 on 2022-03-27 01:11 - -import InvenTree.fields -import django.core.validators -from django.core import serializers -from django.db import migrations, models -import django.db.models.deletion -import djmoney.models.fields -import djmoney.models.validators - - -def _convert_model(apps, line_item_ref, extra_line_ref, price_ref): - """Convert the OrderLineItem instances if applicable to new ExtraLine instances""" - OrderLineItem = apps.get_model('order', line_item_ref) - OrderExtraLine = apps.get_model('order', extra_line_ref) - - items_to_change = OrderLineItem.objects.filter(part=None) - if items_to_change.count() == 0: - return - - print(f'\nFound {items_to_change.count()} old {line_item_ref} instance(s)') - print(f'Starting to convert - currently at {OrderExtraLine.objects.all().count()} {extra_line_ref} / {OrderLineItem.objects.all().count()} {line_item_ref} instance(s)') - for lineItem in items_to_change: - newitem = OrderExtraLine( - order=lineItem.order, - notes=lineItem.notes, - price=getattr(lineItem, price_ref), - quantity=lineItem.quantity, - reference=lineItem.reference, - ) - newitem.context = {'migration': serializers.serialize('json', [lineItem, ])} - newitem.save() - - lineItem.delete() - print(f'Done converting line items - now at {OrderExtraLine.objects.all().count()} {extra_line_ref} / {OrderLineItem.objects.all().count()} {line_item_ref} instance(s)') - - -def _reconvert_model(apps, line_item_ref, extra_line_ref): # pragma: no cover - """Convert ExtraLine instances back to OrderLineItem instances""" - OrderLineItem = apps.get_model('order', line_item_ref) - OrderExtraLine = apps.get_model('order', extra_line_ref) - - print(f'\nStarting to convert - currently at {OrderExtraLine.objects.all().count()} {extra_line_ref} / {OrderLineItem.objects.all().count()} {line_item_ref} instance(s)') - for extra_line in OrderExtraLine.objects.all(): - # regenreate item - if extra_line.context: - context_string = getattr(extra_line.context, 'migration') - if not context_string: - continue - [item.save() for item in serializers.deserialize('json', context_string)] - extra_line.delete() - print(f'Done converting line items - now at {OrderExtraLine.objects.all().count()} {extra_line_ref} / {OrderLineItem.objects.all().count()} {line_item_ref} instance(s)') - - -def convert_line_items(apps, schema_editor): - """convert line items""" - _convert_model(apps, 'PurchaseOrderLineItem', 'PurchaseOrderExtraLine', 'purchase_price') - _convert_model(apps, 'SalesOrderLineItem', 'SalesOrderExtraLine', 'sale_price') - - -def nunconvert_line_items(apps, schema_editor): # pragma: no cover - """reconvert line items""" - _reconvert_model(apps, 'PurchaseOrderLineItem', 'PurchaseOrderExtraLine') - _reconvert_model(apps, 'SalesOrderLineItem', 'SalesOrderExtraLine') - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0063_alter_purchaseorderlineitem_unique_together'), - ] - - operations = [ - migrations.CreateModel( - name='SalesOrderExtraLine', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('quantity', InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Item quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity')), - ('reference', models.CharField(blank=True, help_text='Line item reference', max_length=100, verbose_name='Reference')), - ('notes', models.CharField(blank=True, help_text='Line item notes', max_length=500, verbose_name='Notes')), - ('target_date', models.DateField(blank=True, help_text='Target shipping date for this line item', null=True, verbose_name='Target Date')), - ('context', models.JSONField(blank=True, help_text='Additional context for this line', null=True, verbose_name='Context')), - ('price_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('price', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=4, default_currency='', help_text='Unit price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price')), - ('order', models.ForeignKey(help_text='Sales Order', on_delete=django.db.models.deletion.CASCADE, related_name='extra_lines', to='order.salesorder', verbose_name='Order')), - ], - options={ - 'abstract': False, - 'verbose_name': 'Sales Order Extra Line', - }, - ), - migrations.CreateModel( - name='PurchaseOrderExtraLine', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('quantity', InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Item quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity')), - ('reference', models.CharField(blank=True, help_text='Line item reference', max_length=100, verbose_name='Reference')), - ('notes', models.CharField(blank=True, help_text='Line item notes', max_length=500, verbose_name='Notes')), - ('target_date', models.DateField(blank=True, help_text='Target shipping date for this line item', null=True, verbose_name='Target Date')), - ('context', models.JSONField(blank=True, help_text='Additional context for this line', null=True, verbose_name='Context')), - ('price_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('price', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=4, default_currency='', help_text='Unit price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price')), - ('order', models.ForeignKey(help_text='Purchase Order', on_delete=django.db.models.deletion.CASCADE, related_name='extra_lines', to='order.purchaseorder', verbose_name='Order')), - ], - options={ - 'abstract': False, - 'verbose_name': 'Purchase Order Extra Line', - }, - ), - migrations.RunPython(convert_line_items, reverse_code=nunconvert_line_items), - ] diff --git a/src/backend/InvenTree/order/migrations/0065_alter_purchaseorderlineitem_part.py b/src/backend/InvenTree/order/migrations/0065_alter_purchaseorderlineitem_part.py deleted file mode 100644 index 033b333f27..0000000000 --- a/src/backend/InvenTree/order/migrations/0065_alter_purchaseorderlineitem_part.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.12 on 2022-03-28 22:02 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0042_supplierpricebreak_updated'), - ('order', '0064_purchaseorderextraline_salesorderextraline'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorderlineitem', - name='part', - field=models.ForeignKey(help_text='Supplier part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='purchase_order_line_items', to='company.supplierpart', verbose_name='Part'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0066_alter_purchaseorder_supplier.py b/src/backend/InvenTree/order/migrations/0066_alter_purchaseorder_supplier.py deleted file mode 100644 index d37aedaeda..0000000000 --- a/src/backend/InvenTree/order/migrations/0066_alter_purchaseorder_supplier.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.13 on 2022-04-30 22:11 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0042_supplierpricebreak_updated'), - ('order', '0065_alter_purchaseorderlineitem_part'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorder', - name='supplier', - field=models.ForeignKey(help_text='Company from which the items are being ordered', limit_choices_to={'is_supplier': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='purchase_orders', to='company.company', verbose_name='Supplier'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0067_auto_20220516_1120.py b/src/backend/InvenTree/order/migrations/0067_auto_20220516_1120.py deleted file mode 100644 index 0c5409cf35..0000000000 --- a/src/backend/InvenTree/order/migrations/0067_auto_20220516_1120.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.13 on 2022-05-16 11:20 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0066_alter_purchaseorder_supplier'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorder', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - migrations.AddField( - model_name='salesorder', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0068_alter_salesorderallocation_unique_together.py b/src/backend/InvenTree/order/migrations/0068_alter_salesorderallocation_unique_together.py deleted file mode 100644 index 23915cf9d0..0000000000 --- a/src/backend/InvenTree/order/migrations/0068_alter_salesorderallocation_unique_together.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.2.13 on 2022-05-16 14:35 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0067_auto_20220516_1120'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='salesorderallocation', - unique_together=set(), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0069_auto_20220524_0508.py b/src/backend/InvenTree/order/migrations/0069_auto_20220524_0508.py deleted file mode 100644 index ff37522332..0000000000 --- a/src/backend/InvenTree/order/migrations/0069_auto_20220524_0508.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.13 on 2022-05-24 05:08 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0068_alter_salesorderallocation_unique_together'), - ] - - operations = [ - migrations.AddField( - model_name='salesordershipment', - name='invoice_number', - field=models.CharField(blank=True, help_text='Reference number for associated invoice', max_length=100, verbose_name='Invoice Number'), - ), - migrations.AddField( - model_name='salesordershipment', - name='link', - field=models.URLField(blank=True, help_text='Link to external page', verbose_name='Link'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0070_auto_20220620_0728.py b/src/backend/InvenTree/order/migrations/0070_auto_20220620_0728.py deleted file mode 100644 index 660a68fcce..0000000000 --- a/src/backend/InvenTree/order/migrations/0070_auto_20220620_0728.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-20 07:28 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0069_auto_20220524_0508'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorder', - name='notes', - field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Order notes', max_length=50000, null=True, verbose_name='Notes'), - ), - migrations.AlterField( - model_name='salesorder', - name='notes', - field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Order notes', max_length=50000, null=True, verbose_name='Notes'), - ), - migrations.AlterField( - model_name='salesordershipment', - name='notes', - field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Shipment notes', max_length=50000, null=True, verbose_name='Notes'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0071_auto_20220628_0133.py b/src/backend/InvenTree/order/migrations/0071_auto_20220628_0133.py deleted file mode 100644 index 929c71d008..0000000000 --- a/src/backend/InvenTree/order/migrations/0071_auto_20220628_0133.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-28 01:33 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0077_alter_stockitem_notes'), - ('part', '0079_alter_part_notes'), - ('order', '0070_auto_20220620_0728'), - ] - - operations = [ - migrations.AlterField( - model_name='salesorderallocation', - name='item', - field=models.ForeignKey(help_text='Select stock item to allocate', limit_choices_to={'belongs_to': None, 'part__salable': True, 'part__virtual': False, 'sales_order': None}, on_delete=django.db.models.deletion.CASCADE, related_name='sales_order_allocations', to='stock.stockitem', verbose_name='Item'), - ), - migrations.AlterField( - model_name='salesorderlineitem', - name='part', - field=models.ForeignKey(help_text='Part', limit_choices_to={'salable': True, 'virtual': False}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sales_order_line_items', to='part.part', verbose_name='Part'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0072_alter_salesorder_reference.py b/src/backend/InvenTree/order/migrations/0072_alter_salesorder_reference.py deleted file mode 100644 index 118e2883be..0000000000 --- a/src/backend/InvenTree/order/migrations/0072_alter_salesorder_reference.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.14 on 2022-07-07 11:55 - -from django.db import migrations, models -import order.validators - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0071_auto_20220628_0133'), - ] - - operations = [ - migrations.AlterField( - model_name='salesorder', - name='reference', - field=models.CharField(default=order.validators.generate_next_sales_order_reference, help_text='Order reference', max_length=64, unique=True, validators=[order.validators.validate_sales_order_reference], verbose_name='Reference'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0073_alter_purchaseorder_reference.py b/src/backend/InvenTree/order/migrations/0073_alter_purchaseorder_reference.py deleted file mode 100644 index 1befaaa38f..0000000000 --- a/src/backend/InvenTree/order/migrations/0073_alter_purchaseorder_reference.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.14 on 2022-07-09 01:01 - -from django.db import migrations, models -import order.validators - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0072_alter_salesorder_reference'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorder', - name='reference', - field=models.CharField(default=order.validators.generate_next_purchase_order_reference, help_text='Order reference', max_length=64, unique=True, validators=[order.validators.validate_purchase_order_reference], verbose_name='Reference'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0074_auto_20220709_0108.py b/src/backend/InvenTree/order/migrations/0074_auto_20220709_0108.py deleted file mode 100644 index 4d8e5014c8..0000000000 --- a/src/backend/InvenTree/order/migrations/0074_auto_20220709_0108.py +++ /dev/null @@ -1,102 +0,0 @@ -# Generated by Django 3.2.14 on 2022-07-09 01:08 - -from django.db import migrations - - -def update_order_references(order_model, prefix): - """Update all references of the given model, with the specified prefix""" - - n = 0 - - for order in order_model.objects.all(): - if not order.reference.startswith(prefix): - order.reference = prefix + order.reference - order.save() - - n += 1 - - return n - - -def update_salesorder_reference(apps, schema_editor): - """Migrate the reference pattern for the SalesOrder model""" - - # Extract the existing "prefix" value - InvenTreeSetting = apps.get_model('common', 'inventreesetting') - - try: - prefix = InvenTreeSetting.objects.get(key='SALESORDER_REFERENCE_PREFIX').value - except Exception: - prefix = 'SO-' - - # Construct a reference pattern - pattern = prefix + '{ref:04d}' - - # Create or update the BuildOrder.reference pattern - try: - setting = InvenTreeSetting.objects.get(key='SALESORDER_REFERENCE_PATTERN') - setting.value = pattern - setting.save() - except InvenTreeSetting.DoesNotExist: - setting = InvenTreeSetting.objects.create( - key='SALESORDER_REFERENCE_PATTERN', - value=pattern, - ) - - # Update any existing sales order references - SalesOrder = apps.get_model('order', 'salesorder') - n = update_order_references(SalesOrder, prefix) - - if n > 0: - print(f"Updated reference field for {n} SalesOrder objects") - - -def update_purchaseorder_reference(apps, schema_editor): - """Migrate the reference pattern for the PurchaseOrder model""" - - # Extract the existing "prefix" value - InvenTreeSetting = apps.get_model('common', 'inventreesetting') - - try: - prefix = InvenTreeSetting.objects.get(key='PURCHASEORDER_REFERENCE_PREFIX').value - except Exception: - prefix = 'PO-' - - # Construct a reference pattern - pattern = prefix + '{ref:04d}' - - # Create or update the BuildOrder.reference pattern - try: - setting = InvenTreeSetting.objects.get(key='PURCHASEORDER_REFERENCE_PATTERN') - setting.value = pattern - setting.save() - except InvenTreeSetting.DoesNotExist: - setting = InvenTreeSetting.objects.create( - key='PURCHASEORDER_REFERENCE_PATTERN', - value=pattern, - ) - - # Update any existing sales order references - PurchaseOrder = apps.get_model('order', 'purchaseorder') - n = update_order_references(PurchaseOrder, prefix) - - if n > 0: - print(f"Updated reference field for {n} PurchaseOrder objects") - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0073_alter_purchaseorder_reference'), - ] - - operations = [ - migrations.RunPython( - update_salesorder_reference, - reverse_code=migrations.RunPython.noop, - ), - migrations.RunPython( - update_purchaseorder_reference, - reverse_code=migrations.RunPython.noop, - ) - ] diff --git a/src/backend/InvenTree/order/migrations/0075_auto_20221110_0108.py b/src/backend/InvenTree/order/migrations/0075_auto_20221110_0108.py deleted file mode 100644 index 11a2737ff4..0000000000 --- a/src/backend/InvenTree/order/migrations/0075_auto_20221110_0108.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 3.2.16 on 2022-11-10 01:08 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0074_auto_20220709_0108'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorder', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', verbose_name='Link'), - ), - migrations.AlterField( - model_name='salesorder', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', verbose_name='Link'), - ), - migrations.AlterField( - model_name='salesordershipment', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', verbose_name='Link'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0076_auto_20221111_0153.py b/src/backend/InvenTree/order/migrations/0076_auto_20221111_0153.py deleted file mode 100644 index c00d950b5d..0000000000 --- a/src/backend/InvenTree/order/migrations/0076_auto_20221111_0153.py +++ /dev/null @@ -1,35 +0,0 @@ -# Generated by Django 3.2.16 on 2022-11-11 01:53 - -import InvenTree.fields -from django.db import migrations -import djmoney.models.validators - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0075_auto_20221110_0108'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorderextraline', - name='price', - field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Unit price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price'), - ), - migrations.AlterField( - model_name='purchaseorderlineitem', - name='purchase_price', - field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Unit purchase price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Purchase Price'), - ), - migrations.AlterField( - model_name='salesorderextraline', - name='price', - field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Unit price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price'), - ), - migrations.AlterField( - model_name='salesorderlineitem', - name='sale_price', - field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Unit sale price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Sale Price'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0077_auto_20230129_0154.py b/src/backend/InvenTree/order/migrations/0077_auto_20230129_0154.py deleted file mode 100644 index 122e6248fb..0000000000 --- a/src/backend/InvenTree/order/migrations/0077_auto_20230129_0154.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 3.2.16 on 2023-01-29 01:54 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0076_auto_20221111_0153'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorderextraline', - name='target_date', - field=models.DateField(blank=True, help_text='Target date for this line item (leave blank to use the target date from the order)', null=True, verbose_name='Target Date'), - ), - migrations.AlterField( - model_name='purchaseorderlineitem', - name='target_date', - field=models.DateField(blank=True, help_text='Target date for this line item (leave blank to use the target date from the order)', null=True, verbose_name='Target Date'), - ), - migrations.AlterField( - model_name='salesorderextraline', - name='target_date', - field=models.DateField(blank=True, help_text='Target date for this line item (leave blank to use the target date from the order)', null=True, verbose_name='Target Date'), - ), - migrations.AlterField( - model_name='salesorderlineitem', - name='target_date', - field=models.DateField(blank=True, help_text='Target date for this line item (leave blank to use the target date from the order)', null=True, verbose_name='Target Date'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0078_auto_20230304_0721.py b/src/backend/InvenTree/order/migrations/0078_auto_20230304_0721.py deleted file mode 100644 index 3c06a60017..0000000000 --- a/src/backend/InvenTree/order/migrations/0078_auto_20230304_0721.py +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-04 07:21 - -import InvenTree.fields -from django.db import migrations -import djmoney.models.fields -import djmoney.models.validators - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0077_auto_20230129_0154'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorder', - name='total_price', - field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Total price for this order', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Total Price'), - ), - migrations.AddField( - model_name='purchaseorder', - name='total_price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3), - ), - migrations.AddField( - model_name='salesorder', - name='total_price', - field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Total price for this order', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Total Price'), - ), - migrations.AddField( - model_name='salesorder', - name='total_price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0079_auto_20230304_0904.py b/src/backend/InvenTree/order/migrations/0079_auto_20230304_0904.py deleted file mode 100644 index ffc27b6b8e..0000000000 --- a/src/backend/InvenTree/order/migrations/0079_auto_20230304_0904.py +++ /dev/null @@ -1,126 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-04 09:04 - -import logging - -from django.db import migrations - -from djmoney.contrib.exchange.exceptions import MissingRate -from djmoney.contrib.exchange.models import convert_money -from djmoney.money import Money - -from common.currency import currency_code_default - - -logger = logging.getLogger('inventree') - - -def update_purchase_order_price(apps, schema_editor): - """Calculate 'total_price' field for each PurchaseOrder""" - - PurchaseOrder = apps.get_model('order', 'purchaseorder') - - currency = currency_code_default() - - valid_count = 0 - invalid_count = 0 - - for order in PurchaseOrder.objects.all(): - - valid = True - - total_price = Money(0, currency) - - for line in order.lines.all(): - if line.purchase_price: - try: - total_price += convert_money(line.purchase_price, currency) * line.quantity - except MissingRate: - valid = False - break - - for line in order.extra_lines.all(): - if line.price: - try: - total_price += convert_money(line.price, currency) * line.quantity - except MissingRate: - valid = False - break - - if valid: - order.total_price = total_price - order.save() - - valid_count += 1 - else: - invalid_count +=1 - - if valid_count > 0: - logger.info(f"Updated 'total_price' field for {valid_count} PurchaseOrder instances") - - if invalid_count > 0: - logger.info(f"'total_price' field could not be updated for {invalid_count} PurchaseOrder instances") - - -def update_sales_order_price(apps, schema_editor): - """Calculate 'total_price' field for each SalesOrder""" - - SalesOrder = apps.get_model('order', 'salesorder') - - currency = currency_code_default() - - valid_count = 0 - invalid_count = 0 - - for order in SalesOrder.objects.all(): - - valid = True - - total_price = Money(0, currency) - - for line in order.lines.all(): - if line.sale_price: - try: - total_price += convert_money(line.sale_price, currency) * line.quantity - except MissingRate: - valid = False - break - - for line in order.extra_lines.all(): - if line.price: - try: - total_price += convert_money(line.price, currency) * line.quantity - except MissingRate: - valid = False - break - - if valid: - order.total_price = total_price - order.save() - - valid_count += 1 - else: - invalid_count +=1 - - if valid_count > 0: - logger.info(f"Updated 'total_price' field for {valid_count} SalesOrder instances") - - if invalid_count > 0: - logger.info(f"'total_price' field could not be updated for {invalid_count} SalesOrder instances") - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0078_auto_20230304_0721'), - ] - - operations = [ - migrations.RunPython( - update_purchase_order_price, - reverse_code=migrations.RunPython.noop - ), - migrations.RunPython( - update_sales_order_price, - reverse_code=migrations.RunPython.noop, - ) - ] diff --git a/src/backend/InvenTree/order/migrations/0080_auto_20230317_0816.py b/src/backend/InvenTree/order/migrations/0080_auto_20230317_0816.py deleted file mode 100644 index 1ea1c4c95e..0000000000 --- a/src/backend/InvenTree/order/migrations/0080_auto_20230317_0816.py +++ /dev/null @@ -1,38 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-17 08:16 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0079_auto_20230304_0904'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorderextraline', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - migrations.AddField( - model_name='purchaseorderlineitem', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - migrations.AddField( - model_name='salesorderextraline', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - migrations.AddField( - model_name='salesorderlineitem', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - migrations.AddField( - model_name='salesordershipment', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0081_auto_20230314_0725.py b/src/backend/InvenTree/order/migrations/0081_auto_20230314_0725.py deleted file mode 100644 index ed815aa5a9..0000000000 --- a/src/backend/InvenTree/order/migrations/0081_auto_20230314_0725.py +++ /dev/null @@ -1,65 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-14 07:25 - -import InvenTree.fields -import InvenTree.models -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import order.validators - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('company', '0054_companyattachment'), - ('users', '0006_alter_ruleset_name'), - ('order', '0080_auto_20230317_0816'), - ] - - operations = [ - migrations.CreateModel( - name='ReturnOrder', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('metadata', models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata')), - ('reference_int', models.BigIntegerField(default=0)), - ('description', models.CharField(help_text='Order description', max_length=250, verbose_name='Description')), - ('link', InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', verbose_name='Link')), - ('creation_date', models.DateField(blank=True, null=True, verbose_name='Creation Date')), - ('notes', InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Order notes', max_length=50000, null=True, verbose_name='Notes')), - ('reference', models.CharField(default=order.validators.generate_next_return_order_reference, help_text='Return Order reference', max_length=64, unique=True, validators=[order.validators.validate_return_order_reference], verbose_name='Reference')), - ('status', models.PositiveIntegerField(choices=[(10, 'Pending'), (30, 'Complete'), (40, 'Cancelled')], default=10, help_text='Return order status', verbose_name='Status')), - ('customer_reference', models.CharField(blank=True, help_text='Customer order reference code', max_length=64, verbose_name='Customer Reference ')), - ('issue_date', models.DateField(blank=True, help_text='Date order was issued', null=True, verbose_name='Issue Date')), - ('complete_date', models.DateField(blank=True, help_text='Date order was completed', null=True, verbose_name='Completion Date')), - ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='Created By')), - ('customer', models.ForeignKey(help_text='Company from which items are being returned', limit_choices_to={'is_customer': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sales_orders', to='company.company', verbose_name='Customer')), - ('responsible', models.ForeignKey(blank=True, help_text='User or group responsible for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='users.owner', verbose_name='Responsible')), - ], - options={ - 'abstract': False, - 'verbose_name': 'Return Order', - }, - ), - migrations.AlterField( - model_name='salesorder', - name='customer', - field=models.ForeignKey(help_text='Company to which the items are being sold', limit_choices_to={'is_customer': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='return_orders', to='company.company', verbose_name='Customer'), - ), - migrations.CreateModel( - name='ReturnOrderAttachment', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('attachment', models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to='attachments', verbose_name='Attachment')), - ('link', InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', null=True, verbose_name='Link')), - ('comment', models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment')), - ('upload_date', models.DateField(auto_now_add=True, null=True, verbose_name='upload date')), - ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='order.returnorder')), - ('user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User')), - ], - options={ - 'abstract': False, - }, - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0082_auto_20230314_1259.py b/src/backend/InvenTree/order/migrations/0082_auto_20230314_1259.py deleted file mode 100644 index 8a9061406c..0000000000 --- a/src/backend/InvenTree/order/migrations/0082_auto_20230314_1259.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-14 12:59 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0054_companyattachment'), - ('order', '0081_auto_20230314_0725'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorder', - name='contact', - field=models.ForeignKey(blank=True, help_text='Point of contact for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='company.contact', verbose_name='Contact'), - ), - migrations.AddField( - model_name='returnorder', - name='contact', - field=models.ForeignKey(blank=True, help_text='Point of contact for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='company.contact', verbose_name='Contact'), - ), - migrations.AddField( - model_name='salesorder', - name='contact', - field=models.ForeignKey(blank=True, help_text='Point of contact for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='company.contact', verbose_name='Contact'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0083_returnorderextraline.py b/src/backend/InvenTree/order/migrations/0083_returnorderextraline.py deleted file mode 100644 index dd6bf69767..0000000000 --- a/src/backend/InvenTree/order/migrations/0083_returnorderextraline.py +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-16 02:52 - -import InvenTree.fields -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion -import djmoney.models.fields -import djmoney.models.validators - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0082_auto_20230314_1259'), - ] - - operations = [ - migrations.CreateModel( - name='ReturnOrderExtraLine', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('quantity', InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Item quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity')), - ('reference', models.CharField(blank=True, help_text='Line item reference', max_length=100, verbose_name='Reference')), - ('notes', models.CharField(blank=True, help_text='Line item notes', max_length=500, verbose_name='Notes')), - ('target_date', models.DateField(blank=True, help_text='Target date for this line item (leave blank to use the target date from the order)', null=True, verbose_name='Target Date')), - ('context', models.JSONField(blank=True, help_text='Additional context for this line', null=True, verbose_name='Context')), - ('price_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('price', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Unit price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price')), - ('order', models.ForeignKey(help_text='Return Order', on_delete=django.db.models.deletion.CASCADE, related_name='extra_lines', to='order.returnorder', verbose_name='Order')), - ], - options={ - 'abstract': False, - 'verbose_name': 'Return Order Extra Line', - }, - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0084_auto_20230321_1111.py b/src/backend/InvenTree/order/migrations/0084_auto_20230321_1111.py deleted file mode 100644 index 49290b4349..0000000000 --- a/src/backend/InvenTree/order/migrations/0084_auto_20230321_1111.py +++ /dev/null @@ -1,39 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-21 11:11 - -from django.db import migrations, models - -from order.status_codes import ReturnOrderStatus - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0083_returnorderextraline'), - ] - - operations = [ - migrations.AddField( - model_name='returnorder', - name='target_date', - field=models.DateField(blank=True, help_text='Expected date for order delivery. Order will be overdue after this date.', null=True, verbose_name='Target Date'), - ), - migrations.AlterField( - model_name='purchaseorder', - name='target_date', - field=models.DateField(blank=True, help_text='Expected date for order delivery. Order will be overdue after this date.', null=True, verbose_name='Target Date'), - ), - migrations.AlterField( - model_name='returnorder', - name='status', - field=models.PositiveIntegerField( - choices=ReturnOrderStatus.items(), - default=ReturnOrderStatus.PENDING.value, - help_text='Return order status', verbose_name='Status' - ), - ), - migrations.AlterField( - model_name='salesorder', - name='target_date', - field=models.DateField(blank=True, help_text='Expected date for order delivery. Order will be overdue after this date.', null=True, verbose_name='Target Date'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0085_auto_20230322_1056.py b/src/backend/InvenTree/order/migrations/0085_auto_20230322_1056.py deleted file mode 100644 index 283af0886b..0000000000 --- a/src/backend/InvenTree/order/migrations/0085_auto_20230322_1056.py +++ /dev/null @@ -1,50 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-22 10:56 - -import InvenTree.fields -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion -import djmoney.models.fields -import djmoney.models.validators - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0095_stocklocation_external'), - ('order', '0084_auto_20230321_1111'), - ] - - operations = [ - migrations.AddField( - model_name='returnorder', - name='total_price', - field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Total price for this order', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Total Price'), - ), - migrations.AddField( - model_name='returnorder', - name='total_price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3), - ), - migrations.CreateModel( - name='ReturnOrderLineItem', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('quantity', InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Item quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity')), - ('reference', models.CharField(blank=True, help_text='Line item reference', max_length=100, verbose_name='Reference')), - ('notes', models.CharField(blank=True, help_text='Line item notes', max_length=500, verbose_name='Notes')), - ('target_date', models.DateField(blank=True, help_text='Target date for this line item (leave blank to use the target date from the order)', null=True, verbose_name='Target Date')), - ('received_date', models.DateField(blank=True, help_text='The date this return item was received', null=True, verbose_name='Received Date')), - ('outcome', models.PositiveIntegerField(choices=[(10, 'Pending'), (20, 'Return'), (30, 'Repair'), (50, 'Refund'), (40, 'Replace'), (60, 'Reject')], default=10, help_text='Outcome for this line item', verbose_name='Outcome')), - ('price_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('price', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Cost associated with return or repair for this line item', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price')), - ('link', InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', verbose_name='Link')), - ('item', models.ForeignKey(help_text='Select item to return from customer', on_delete=django.db.models.deletion.CASCADE, related_name='return_order_lines', to='stock.stockitem', verbose_name='Item')), - ('order', models.ForeignKey(help_text='Return Order', on_delete=django.db.models.deletion.CASCADE, related_name='lines', to='order.returnorder', verbose_name='Order')), - ], - options={ - 'unique_together': {('order', 'item')}, - 'verbose_name': 'Return Order Line Item', - }, - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0086_auto_20230323_1108.py b/src/backend/InvenTree/order/migrations/0086_auto_20230323_1108.py deleted file mode 100644 index 3cfcd2f629..0000000000 --- a/src/backend/InvenTree/order/migrations/0086_auto_20230323_1108.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-23 11:08 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0085_auto_20230322_1056'), - ] - - operations = [ - migrations.AddField( - model_name='returnorderextraline', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - migrations.AddField( - model_name='returnorderlineitem', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0087_alter_salesorder_status.py b/src/backend/InvenTree/order/migrations/0087_alter_salesorder_status.py deleted file mode 100644 index dd6c0326bf..0000000000 --- a/src/backend/InvenTree/order/migrations/0087_alter_salesorder_status.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-30 11:50 - -from django.db import migrations, models - -from order.status_codes import SalesOrderStatus - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0086_auto_20230323_1108'), - ] - - operations = [ - migrations.AlterField( - model_name='salesorder', - name='status', - field=models.PositiveIntegerField( - choices=SalesOrderStatus.items(), - default=SalesOrderStatus.PENDING.value, - help_text='Sales order status', verbose_name='Status' - ), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0088_auto_20230403_1402.py b/src/backend/InvenTree/order/migrations/0088_auto_20230403_1402.py deleted file mode 100644 index ae30fb4278..0000000000 --- a/src/backend/InvenTree/order/migrations/0088_auto_20230403_1402.py +++ /dev/null @@ -1,39 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-03 14:02 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0087_alter_salesorder_status'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorderextraline', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', verbose_name='Link'), - ), - migrations.AddField( - model_name='purchaseorderlineitem', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', verbose_name='Link'), - ), - migrations.AddField( - model_name='returnorderextraline', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', verbose_name='Link'), - ), - migrations.AddField( - model_name='salesorderextraline', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', verbose_name='Link'), - ), - migrations.AddField( - model_name='salesorderlineitem', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page', verbose_name='Link'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0089_auto_20230404_0030.py b/src/backend/InvenTree/order/migrations/0089_auto_20230404_0030.py deleted file mode 100644 index a28439003b..0000000000 --- a/src/backend/InvenTree/order/migrations/0089_auto_20230404_0030.py +++ /dev/null @@ -1,43 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-04 00:30 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0088_auto_20230403_1402'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorder', - name='barcode_data', - field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), - ), - migrations.AddField( - model_name='purchaseorder', - name='barcode_hash', - field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), - ), - migrations.AddField( - model_name='returnorder', - name='barcode_data', - field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), - ), - migrations.AddField( - model_name='returnorder', - name='barcode_hash', - field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), - ), - migrations.AddField( - model_name='salesorder', - name='barcode_data', - field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), - ), - migrations.AddField( - model_name='salesorder', - name='barcode_hash', - field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0090_auto_20230412_1752.py b/src/backend/InvenTree/order/migrations/0090_auto_20230412_1752.py deleted file mode 100644 index e337d6c949..0000000000 --- a/src/backend/InvenTree/order/migrations/0090_auto_20230412_1752.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-12 17:52 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0089_auto_20230404_0030'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorder', - name='description', - field=models.CharField(blank=True, help_text='Order description (optional)', max_length=250, verbose_name='Description'), - ), - migrations.AlterField( - model_name='returnorder', - name='description', - field=models.CharField(blank=True, help_text='Order description (optional)', max_length=250, verbose_name='Description'), - ), - migrations.AlterField( - model_name='salesorder', - name='description', - field=models.CharField(blank=True, help_text='Order description (optional)', max_length=250, verbose_name='Description'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0091_auto_20230419_0037.py b/src/backend/InvenTree/order/migrations/0091_auto_20230419_0037.py deleted file mode 100644 index 3e420c0d98..0000000000 --- a/src/backend/InvenTree/order/migrations/0091_auto_20230419_0037.py +++ /dev/null @@ -1,34 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-19 00:37 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0090_auto_20230412_1752'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorder', - name='notes', - field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), - ), - migrations.AlterField( - model_name='returnorder', - name='notes', - field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), - ), - migrations.AlterField( - model_name='salesorder', - name='notes', - field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), - ), - migrations.AlterField( - model_name='salesordershipment', - name='notes', - field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0092_auto_20230419_0250.py b/src/backend/InvenTree/order/migrations/0092_auto_20230419_0250.py deleted file mode 100644 index d2b4aa2aef..0000000000 --- a/src/backend/InvenTree/order/migrations/0092_auto_20230419_0250.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-19 02:50 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0018_projectcode'), - ('order', '0091_auto_20230419_0037'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorder', - name='project_code', - field=models.ForeignKey(blank=True, help_text='Select project code for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.projectcode', verbose_name='Project Code'), - ), - migrations.AddField( - model_name='returnorder', - name='project_code', - field=models.ForeignKey(blank=True, help_text='Select project code for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.projectcode', verbose_name='Project Code'), - ), - migrations.AddField( - model_name='salesorder', - name='project_code', - field=models.ForeignKey(blank=True, help_text='Select project code for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.projectcode', verbose_name='Project Code'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0093_auto_20230426_0248.py b/src/backend/InvenTree/order/migrations/0093_auto_20230426_0248.py deleted file mode 100644 index cefb7ed724..0000000000 --- a/src/backend/InvenTree/order/migrations/0093_auto_20230426_0248.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-26 02:48 - -import InvenTree.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0092_auto_20230419_0250'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorder', - name='order_currency', - field=models.CharField(blank=True, help_text='Currency for this order (leave blank to use company default)', max_length=3, null=True, validators=[InvenTree.validators.validate_currency_code], verbose_name='Order Currency'), - ), - migrations.AddField( - model_name='returnorder', - name='order_currency', - field=models.CharField(blank=True, help_text='Currency for this order (leave blank to use company default)', max_length=3, null=True, validators=[InvenTree.validators.validate_currency_code], verbose_name='Order Currency'), - ), - migrations.AddField( - model_name='salesorder', - name='order_currency', - field=models.CharField(blank=True, help_text='Currency for this order (leave blank to use company default)', max_length=3, null=True, validators=[InvenTree.validators.validate_currency_code], verbose_name='Order Currency'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0094_auto_20230514_2331.py b/src/backend/InvenTree/order/migrations/0094_auto_20230514_2331.py deleted file mode 100644 index 31771d09a3..0000000000 --- a/src/backend/InvenTree/order/migrations/0094_auto_20230514_2331.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-14 23:31 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0093_auto_20230426_0248'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorderextraline', - name='description', - field=models.CharField(blank=True, help_text='Line item description (optional)', max_length=250, verbose_name='Description'), - ), - migrations.AddField( - model_name='returnorderextraline', - name='description', - field=models.CharField(blank=True, help_text='Line item description (optional)', max_length=250, verbose_name='Description'), - ), - migrations.AddField( - model_name='salesorderextraline', - name='description', - field=models.CharField(blank=True, help_text='Line item description (optional)', max_length=250, verbose_name='Description'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0095_salesordershipment_delivery_date.py b/src/backend/InvenTree/order/migrations/0095_salesordershipment_delivery_date.py deleted file mode 100644 index fb825b5858..0000000000 --- a/src/backend/InvenTree/order/migrations/0095_salesordershipment_delivery_date.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-13 06:58 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0094_auto_20230514_2331'), - ] - - operations = [ - migrations.AddField( - model_name='salesordershipment', - name='delivery_date', - field=models.DateField(blank=True, help_text='Date of delivery of shipment', null=True, verbose_name='Delivery Date'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0096_alter_returnorderlineitem_outcome.py b/src/backend/InvenTree/order/migrations/0096_alter_returnorderlineitem_outcome.py deleted file mode 100644 index 9c80796a6f..0000000000 --- a/src/backend/InvenTree/order/migrations/0096_alter_returnorderlineitem_outcome.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.19 on 2023-06-04 17:43 - -from django.db import migrations, models -import order.status_codes - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0095_salesordershipment_delivery_date'), - ] - - operations = [ - migrations.AlterField( - model_name='returnorderlineitem', - name='outcome', - field=models.PositiveIntegerField(choices=order.status_codes.ReturnOrderLineStatus.items(), default=10, help_text='Outcome for this line item', verbose_name='Outcome'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0097_auto_20230529_0107.py b/src/backend/InvenTree/order/migrations/0097_auto_20230529_0107.py deleted file mode 100644 index cacf50ba4c..0000000000 --- a/src/backend/InvenTree/order/migrations/0097_auto_20230529_0107.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-29 01:07 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0065_remove_company_address'), - ('order', '0096_alter_returnorderlineitem_outcome'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorder', - name='address', - field=models.ForeignKey(blank=True, help_text='Company address for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='company.address', verbose_name='Address'), - ), - migrations.AddField( - model_name='returnorder', - name='address', - field=models.ForeignKey(blank=True, help_text='Company address for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='company.address', verbose_name='Address'), - ), - migrations.AddField( - model_name='salesorder', - name='address', - field=models.ForeignKey(blank=True, help_text='Company address for this order', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='company.address', verbose_name='Address'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0098_auto_20231024_1844.py b/src/backend/InvenTree/order/migrations/0098_auto_20231024_1844.py deleted file mode 100644 index 99f5cbe7dc..0000000000 --- a/src/backend/InvenTree/order/migrations/0098_auto_20231024_1844.py +++ /dev/null @@ -1,59 +0,0 @@ -# Generated by Django 3.2.22 on 2023-10-24 16:44 - -from django.db import migrations -import djmoney.models.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0097_auto_20230529_0107'), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorder', - name='total_price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='purchaseorderextraline', - name='price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='purchaseorderlineitem', - name='purchase_price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='returnorder', - name='total_price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='returnorderextraline', - name='price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='returnorderlineitem', - name='price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='salesorder', - name='total_price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='salesorderextraline', - name='price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='salesorderlineitem', - name='sale_price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0099_alter_salesorder_status.py b/src/backend/InvenTree/order/migrations/0099_alter_salesorder_status.py deleted file mode 100644 index bc5d52f44d..0000000000 --- a/src/backend/InvenTree/order/migrations/0099_alter_salesorder_status.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 4.2.11 on 2024-04-03 00:40 - -import django.core.validators -from django.db import migrations, models -import order.status_codes - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0098_auto_20231024_1844'), - ] - - operations = [ - migrations.AlterField( - model_name='salesorder', - name='status', - field=models.PositiveIntegerField( - choices=order.status_codes.SalesOrderStatus.items(), - default=order.status_codes.SalesOrderStatus.PENDING.value, - help_text='Sales order status', verbose_name='Status' - ), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0100_remove_returnorderattachment_order_and_more.py b/src/backend/InvenTree/order/migrations/0100_remove_returnorderattachment_order_and_more.py deleted file mode 100644 index 01e5679ebd..0000000000 --- a/src/backend/InvenTree/order/migrations/0100_remove_returnorderattachment_order_and_more.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 4.2.12 on 2024-06-09 09:02 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0050_auto_20240508_0138'), - ('common', '0026_auto_20240608_1238'), - ('company', '0069_company_active'), - ('order', '0099_alter_salesorder_status'), - ('part', '0123_parttesttemplate_choices'), - ('stock', '0110_alter_stockitemtestresult_finished_datetime_and_more') - ] - - operations = [ - migrations.DeleteModel( - name='PurchaseOrderAttachment', - ), - migrations.DeleteModel( - name='ReturnOrderAttachment', - ), - migrations.DeleteModel( - name='SalesOrderAttachment', - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0101_purchaseorder_status_custom_key_and_more.py b/src/backend/InvenTree/order/migrations/0101_purchaseorder_status_custom_key_and_more.py deleted file mode 100644 index 7a36892d73..0000000000 --- a/src/backend/InvenTree/order/migrations/0101_purchaseorder_status_custom_key_and_more.py +++ /dev/null @@ -1,141 +0,0 @@ -# Generated by Django 4.2.14 on 2024-08-07 22:40 - -from django.db import migrations - -import generic.states.fields -import generic.states.validators -import InvenTree.status_codes - - -class Migration(migrations.Migration): - - dependencies = [ - ("order", "0100_remove_returnorderattachment_order_and_more"), - ] - - operations = [ - migrations.AddField( - model_name="purchaseorder", - name="status_custom_key", - field=generic.states.fields.ExtraInvenTreeCustomStatusModelField( - blank=True, - default=None, - help_text="Additional status information for this item", - null=True, - verbose_name="Custom status key", - validators=[ - generic.states.validators.CustomStatusCodeValidator( - status_class=InvenTree.status_codes.PurchaseOrderStatus - ), - ] - ), - ), - migrations.AddField( - model_name="returnorder", - name="status_custom_key", - field=generic.states.fields.ExtraInvenTreeCustomStatusModelField( - blank=True, - default=None, - help_text="Additional status information for this item", - null=True, - verbose_name="Custom status key", - validators=[ - generic.states.validators.CustomStatusCodeValidator( - status_class=InvenTree.status_codes.ReturnOrderStatus - ), - ] - ), - ), - migrations.AddField( - model_name="returnorderlineitem", - name="outcome_custom_key", - field=generic.states.fields.ExtraInvenTreeCustomStatusModelField( - blank=True, - default=None, - help_text="Additional status information for this item", - null=True, - verbose_name="Custom status key", - validators=[ - generic.states.validators.CustomStatusCodeValidator( - status_class=InvenTree.status_codes.ReturnOrderLineStatus - ), - ] - ), - ), - migrations.AddField( - model_name="salesorder", - name="status_custom_key", - field=generic.states.fields.ExtraInvenTreeCustomStatusModelField( - blank=True, - default=None, - help_text="Additional status information for this item", - null=True, - verbose_name="Custom status key", - validators=[ - generic.states.validators.CustomStatusCodeValidator( - status_class=InvenTree.status_codes.SalesOrderStatus - ), - ] - ), - ), - migrations.AlterField( - model_name="purchaseorder", - name="status", - field=generic.states.fields.InvenTreeCustomStatusModelField( - choices=InvenTree.status_codes.PurchaseOrderStatus.items(), - default=10, - help_text="Purchase order status", - verbose_name="Status", - validators=[ - generic.states.validators.CustomStatusCodeValidator( - status_class=InvenTree.status_codes.PurchaseOrderStatus - ), - ] - ), - ), - migrations.AlterField( - model_name="returnorder", - name="status", - field=generic.states.fields.InvenTreeCustomStatusModelField( - choices=InvenTree.status_codes.ReturnOrderStatus.items(), - default=10, - help_text="Return order status", - verbose_name="Status", - validators=[ - generic.states.validators.CustomStatusCodeValidator( - status_class=InvenTree.status_codes.ReturnOrderStatus - ), - ] - ), - ), - migrations.AlterField( - model_name="returnorderlineitem", - name="outcome", - field=generic.states.fields.InvenTreeCustomStatusModelField( - choices=InvenTree.status_codes.ReturnOrderLineStatus.items(), - default=10, - help_text="Outcome for this line item", - verbose_name="Outcome", - validators=[ - generic.states.validators.CustomStatusCodeValidator( - status_class=InvenTree.status_codes.ReturnOrderLineStatus - ), - ] - ), - ), - migrations.AlterField( - model_name="salesorder", - name="status", - field=generic.states.fields.InvenTreeCustomStatusModelField( - choices=InvenTree.status_codes.SalesOrderStatus.items(), - default=10, - help_text="Sales order status", - verbose_name="Status", - validators=[ - generic.states.validators.CustomStatusCodeValidator( - status_class=InvenTree.status_codes.SalesOrderStatus - ), - ] - ), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0102_purchaseorder_destination_and_more.py b/src/backend/InvenTree/order/migrations/0102_purchaseorder_destination_and_more.py deleted file mode 100644 index 9a9ae71d72..0000000000 --- a/src/backend/InvenTree/order/migrations/0102_purchaseorder_destination_and_more.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 4.2.16 on 2024-10-31 02:52 - -from django.db import migrations -import django.db.models.deletion -import mptt.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0113_stockitem_status_custom_key_and_more'), - ('order', '0101_purchaseorder_status_custom_key_and_more'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorder', - name='destination', - field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='purchase_orders', to='stock.stocklocation', verbose_name='Destination', help_text='Destination for received items'), - ), - migrations.AlterField( - model_name='purchaseorderlineitem', - name='destination', - field=mptt.fields.TreeForeignKey(blank=True, help_text='Destination for received items', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='po_lines', to='stock.stocklocation', verbose_name='Destination'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0103_alter_salesorderallocation_shipment.py b/src/backend/InvenTree/order/migrations/0103_alter_salesorderallocation_shipment.py deleted file mode 100644 index c7ec8dcd47..0000000000 --- a/src/backend/InvenTree/order/migrations/0103_alter_salesorderallocation_shipment.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 4.2.16 on 2024-11-06 04:46 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0102_purchaseorder_destination_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='salesorderallocation', - name='shipment', - field=models.ForeignKey(blank=True, help_text='Sales order shipment reference', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='allocations', to='order.salesordershipment', verbose_name='Shipment'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0104_alter_returnorderlineitem_quantity.py b/src/backend/InvenTree/order/migrations/0104_alter_returnorderlineitem_quantity.py deleted file mode 100644 index b77796bf71..0000000000 --- a/src/backend/InvenTree/order/migrations/0104_alter_returnorderlineitem_quantity.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 4.2.16 on 2024-11-29 00:37 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0103_alter_salesorderallocation_shipment'), - ] - - operations = [ - migrations.AlterField( - model_name='returnorderlineitem', - name='quantity', - field=models.DecimalField(decimal_places=5, default=1, help_text='Quantity to return', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0105_auto_20241128_0431.py b/src/backend/InvenTree/order/migrations/0105_auto_20241128_0431.py deleted file mode 100644 index 6e293a3e9a..0000000000 --- a/src/backend/InvenTree/order/migrations/0105_auto_20241128_0431.py +++ /dev/null @@ -1,66 +0,0 @@ -# Generated by Django 4.2.16 on 2024-11-28 04:31 - -from django.db import migrations, connection - - -def update_shipment_date(apps, schema_editor): - """ - Update the shipment date for existing SalesOrderAllocation objects - """ - - from order.status_codes import SalesOrderStatusGroups - - SalesOrder = apps.get_model('order', 'SalesOrder') - - # Find any orders which are "complete" but missing a shipment date - orders = SalesOrder.objects.filter( - status__in=SalesOrderStatusGroups.COMPLETE, - shipment_date__isnull=True - ) - - update_count = 0 - - cursor = connection.cursor() - - for order in orders: - - # Check that the shipment date is actually null here - assert order.shipment_date is None, f"SalesOrder {order.pk} has non-null shipment_date" - - # Find the latest shipment date for any associated allocations - shipments = order.shipments.filter(shipment_date__isnull=False) - latest_shipment = shipments.order_by('-shipment_date').first() - - if not latest_shipment: - continue - - # Update the order with the new shipment date - shipment_date = latest_shipment.shipment_date - - # Raw SQL to prevent some weird migration "order of operations" issues - # Reference: https://github.com/inventree/InvenTree/pull/8814 - query = f"UPDATE order_salesorder SET shipment_date = '{shipment_date}' WHERE id = {order.pk}" - cursor.execute(query) - - # Fetch the updated object, check that the shipment date has been updated - order.refresh_from_db() - assert order.shipment_date is not None, f"SalesOrder {order.pk} still has missing shipment_date" - - update_count += 1 - - if update_count > 0: - print(f"Updated {update_count} SalesOrder shipment dates") - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0104_alter_returnorderlineitem_quantity'), - ] - - operations = [ - migrations.RunPython( - update_shipment_date, - reverse_code=migrations.RunPython.noop - ) - ] diff --git a/src/backend/InvenTree/order/migrations/0106_purchaseorder_start_date_returnorder_start_date_and_more.py b/src/backend/InvenTree/order/migrations/0106_purchaseorder_start_date_returnorder_start_date_and_more.py deleted file mode 100644 index d238723629..0000000000 --- a/src/backend/InvenTree/order/migrations/0106_purchaseorder_start_date_returnorder_start_date_and_more.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 4.2.18 on 2025-01-27 12:50 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0105_auto_20241128_0431'), - ] - - operations = [ - migrations.AddField( - model_name='purchaseorder', - name='start_date', - field=models.DateField(blank=True, help_text='Scheduled start date for this order', null=True, verbose_name='Start date'), - ), - migrations.AddField( - model_name='returnorder', - name='start_date', - field=models.DateField(blank=True, help_text='Scheduled start date for this order', null=True, verbose_name='Start date'), - ), - migrations.AddField( - model_name='salesorder', - name='start_date', - field=models.DateField(blank=True, help_text='Scheduled start date for this order', null=True, verbose_name='Start date'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0107_auto_20250221_1241.py b/src/backend/InvenTree/order/migrations/0107_auto_20250221_1241.py deleted file mode 100644 index 5a4bff1627..0000000000 --- a/src/backend/InvenTree/order/migrations/0107_auto_20250221_1241.py +++ /dev/null @@ -1,64 +0,0 @@ -# Generated by Django 4.2.19 on 2025-02-21 12:41 - -import InvenTree.fields -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("order", "0106_purchaseorder_start_date_returnorder_start_date_and_more"), - ] - - operations = [ - migrations.AlterField( - model_name='purchaseorder', - name='link', - field=models.TextField(null=True, blank=True) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - migrations.AlterField( - model_name='salesorder', - name='link', - field=models.TextField(null=True, blank=True) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - migrations.AlterField( - model_name='returnorder', - name='link', - field=models.TextField(null=True, blank=True) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - migrations.AlterField( - model_name='purchaseorderlineitem', - name='link', - field=models.TextField(null=True, blank=True) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - migrations.AlterField( - model_name='salesorderlineitem', - name='link', - field=models.TextField(null=True, blank=True) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - migrations.AlterField( - model_name='returnorderlineitem', - name='link', - field=models.TextField(null=True, blank=True) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - migrations.AlterField( - model_name='purchaseorderextraline', - name='link', - field=models.TextField(null=True, blank=True) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - migrations.AlterField( - model_name='salesorderextraline', - name='link', - field=models.TextField(null=True, blank=True) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - migrations.AlterField( - model_name='returnorderextraline', - name='link', - field=models.TextField(null=True, blank=True) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - migrations.AlterField( - model_name='salesordershipment', - name='link', - field=models.TextField(null=True, blank=True) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0108_alter_purchaseorder_link_and_more_squashed_0109_alter_purchaseorderextraline_link_and_more.py b/src/backend/InvenTree/order/migrations/0108_alter_purchaseorder_link_and_more_squashed_0109_alter_purchaseorderextraline_link_and_more.py deleted file mode 100644 index bb7a0d36ac..0000000000 --- a/src/backend/InvenTree/order/migrations/0108_alter_purchaseorder_link_and_more_squashed_0109_alter_purchaseorderextraline_link_and_more.py +++ /dev/null @@ -1,114 +0,0 @@ -# Generated by Django 4.2.19 on 2025-02-21 14:57 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("order", "0107_auto_20250221_1241"), - ] - - operations = [ - migrations.AlterField( - model_name="purchaseorder", - name="link", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="Link to external page", - max_length=2000, - verbose_name="Link", - ), - ), - migrations.AlterField( - model_name="purchaseorderlineitem", - name="link", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="Link to external page", - max_length=2000, - verbose_name="Link", - ), - ), - migrations.AlterField( - model_name="returnorder", - name="link", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="Link to external page", - max_length=2000, - verbose_name="Link", - ), - ), - migrations.AlterField( - model_name="returnorderlineitem", - name="link", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="Link to external page", - max_length=2000, - verbose_name="Link", - ), - ), - migrations.AlterField( - model_name="salesorder", - name="link", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="Link to external page", - max_length=2000, - verbose_name="Link", - ), - ), - migrations.AlterField( - model_name="salesorderlineitem", - name="link", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="Link to external page", - max_length=2000, - verbose_name="Link", - ), - ), - migrations.AlterField( - model_name="salesordershipment", - name="link", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="Link to external page", - max_length=2000, - verbose_name="Link", - ), - ), - migrations.AlterField( - model_name="purchaseorderextraline", - name="link", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="Link to external page", - max_length=2000, - verbose_name="Link", - ), - ), - migrations.AlterField( - model_name="returnorderextraline", - name="link", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="Link to external page", - max_length=2000, - verbose_name="Link", - ), - ), - migrations.AlterField( - model_name="salesorderextraline", - name="link", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="Link to external page", - max_length=2000, - verbose_name="Link", - ), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0109_salesorder_issue_date.py b/src/backend/InvenTree/order/migrations/0109_salesorder_issue_date.py deleted file mode 100644 index 4d8efd0626..0000000000 --- a/src/backend/InvenTree/order/migrations/0109_salesorder_issue_date.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 4.2.19 on 2025-02-23 04:02 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("order", "0108_alter_purchaseorder_link_and_more_squashed_0109_alter_purchaseorderextraline_link_and_more"), - ] - - operations = [ - migrations.AddField( - model_name="salesorder", - name="issue_date", - field=models.DateField( - blank=True, - help_text="Date order was issued", - null=True, - verbose_name="Issue Date", - ), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0110_salesordershipment_barcode_data_and_more.py b/src/backend/InvenTree/order/migrations/0110_salesordershipment_barcode_data_and_more.py deleted file mode 100644 index 028c64fbcd..0000000000 --- a/src/backend/InvenTree/order/migrations/0110_salesordershipment_barcode_data_and_more.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 4.2.20 on 2025-04-11 06:37 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0109_salesorder_issue_date'), - ] - - operations = [ - migrations.AddField( - model_name='salesordershipment', - name='barcode_data', - field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), - ), - migrations.AddField( - model_name='salesordershipment', - name='barcode_hash', - field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0111_purchaseorderlineitem_build_order.py b/src/backend/InvenTree/order/migrations/0111_purchaseorderlineitem_build_order.py deleted file mode 100644 index 57b1c5147d..0000000000 --- a/src/backend/InvenTree/order/migrations/0111_purchaseorderlineitem_build_order.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 4.2.20 on 2025-04-11 04:59 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("build", "0057_build_external"), - ("order", "0110_salesordershipment_barcode_data_and_more"), - ] - - operations = [ - migrations.AddField( - model_name="purchaseorderlineitem", - name="build_order", - field=models.ForeignKey( - blank=True, - help_text="External Build Order to be fulfilled by this line item", - limit_choices_to={"external": True}, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="external_line_items", - to="build.build", - verbose_name="Build Order", - ), - ), - ] diff --git a/src/backend/InvenTree/order/migrations/0112_alter_salesorderlineitem_part.py b/src/backend/InvenTree/order/migrations/0112_alter_salesorderlineitem_part.py deleted file mode 100644 index 6151608ff0..0000000000 --- a/src/backend/InvenTree/order/migrations/0112_alter_salesorderlineitem_part.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 4.2.23 on 2025-09-03 04:13 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ("part", "0142_remove_part_last_stocktake_remove_partstocktake_note_and_more"), - ("order", "0111_purchaseorderlineitem_build_order"), - ] - - operations = [ - migrations.AlterField( - model_name="salesorderlineitem", - name="part", - field=models.ForeignKey( - help_text="Part", - limit_choices_to={"salable": True}, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="sales_order_line_items", - to="part.part", - verbose_name="Part", - ), - ), - ] diff --git a/src/backend/InvenTree/order/test_migrations.py b/src/backend/InvenTree/order/test_migrations.py deleted file mode 100644 index d79e805596..0000000000 --- a/src/backend/InvenTree/order/test_migrations.py +++ /dev/null @@ -1,249 +0,0 @@ -"""Unit tests for the 'order' model data migrations.""" - -from django_test_migrations.contrib.unittest_case import MigratorTestCase - -from order.status_codes import SalesOrderStatus - - -class TestRefIntMigrations(MigratorTestCase): - """Test entire schema migration.""" - - migrate_from = ('order', '0040_salesorder_target_date') - migrate_to = ('order', '0061_merge_0054_auto_20211201_2139_0060_auto_20211129_1339') - - def prepare(self): - """Create initial data set.""" - # Create a purchase order from a supplier - Company = self.old_state.apps.get_model('company', 'company') - - supplier = Company.objects.create( - name='Supplier A', - description='A great supplier!', - is_supplier=True, - is_customer=True, - ) - - PurchaseOrder = self.old_state.apps.get_model('order', 'purchaseorder') - SalesOrder = self.old_state.apps.get_model('order', 'salesorder') - - # Create some orders - for ii in range(10): - order = PurchaseOrder.objects.create( - supplier=supplier, - reference=f'{ii}-abcde', - description='Just a test order', - ) - - # Initially, the 'reference_int' field is unavailable - with self.assertRaises(AttributeError): - print(order.reference_int) - - sales_order = SalesOrder.objects.create( - customer=supplier, - reference=f'{ii}-xyz', - description='A test sales order', - ) - - # Initially, the 'reference_int' field is unavailable - with self.assertRaises(AttributeError): - print(sales_order.reference_int) - - # Create orders with very large reference values - self.po_pk = PurchaseOrder.objects.create( - supplier=supplier, - reference='999999999999999999999999999999999', - description='Big reference field', - ).pk - - self.so_pk = SalesOrder.objects.create( - customer=supplier, - reference='999999999999999999999999999999999', - description='Big reference field', - ).pk - - def test_ref_field(self): - """Test that the 'reference_int' field has been created and is filled out correctly.""" - PurchaseOrder = self.new_state.apps.get_model('order', 'purchaseorder') - SalesOrder = self.new_state.apps.get_model('order', 'salesorder') - - for ii in range(10): - po = PurchaseOrder.objects.get(reference=f'{ii}-abcde') - so = SalesOrder.objects.get(reference=f'{ii}-xyz') - - # The integer reference field must have been correctly updated - self.assertEqual(po.reference_int, ii) - self.assertEqual(so.reference_int, ii) - - # Tests for orders with overly large reference values - po = PurchaseOrder.objects.get(pk=self.po_pk) - self.assertEqual(po.reference, '999999999999999999999999999999999') - self.assertEqual(po.reference_int, 0x7FFFFFFF) - - so = SalesOrder.objects.get(pk=self.so_pk) - self.assertEqual(so.reference, '999999999999999999999999999999999') - self.assertEqual(so.reference_int, 0x7FFFFFFF) - - -class TestShipmentMigration(MigratorTestCase): - """Test data migration for the "SalesOrderShipment" model.""" - - migrate_from = ('order', '0051_auto_20211014_0623') - migrate_to = ('order', '0055_auto_20211025_0645') - - def prepare(self): - """Create an initial SalesOrder.""" - Company = self.old_state.apps.get_model('company', 'company') - - customer = Company.objects.create( - name='My customer', - description='A customer we sell stuff to', - is_customer=True, - ) - - SalesOrder = self.old_state.apps.get_model('order', 'salesorder') - - for ii in range(5): - order = SalesOrder.objects.create( - reference=f'SO{ii}', - customer=customer, - description='A sales order for stuffs', - status=SalesOrderStatus.PENDING.value, - ) - - order.save() - - # The "shipment" model does not exist yet - with self.assertRaises(LookupError): - self.old_state.apps.get_model('order', 'salesordershipment') - - def test_shipment_creation(self): - """Check that a SalesOrderShipment has been created.""" - SalesOrder = self.new_state.apps.get_model('order', 'salesorder') - Shipment = self.new_state.apps.get_model('order', 'salesordershipment') - - # Check that the correct number of Shipments have been created - self.assertEqual(SalesOrder.objects.count(), 5) - self.assertEqual(Shipment.objects.count(), 5) - - -class TestAdditionalLineMigration(MigratorTestCase): - """Test entire schema migration.""" - - migrate_from = ('order', '0063_alter_purchaseorderlineitem_unique_together') - migrate_to = ('order', '0064_purchaseorderextraline_salesorderextraline') - - def prepare(self): - """Create initial data set.""" - # Create a purchase order from a supplier - Company = self.old_state.apps.get_model('company', 'company') - PurchaseOrder = self.old_state.apps.get_model('order', 'purchaseorder') - Part = self.old_state.apps.get_model('part', 'part') - Supplierpart = self.old_state.apps.get_model('company', 'supplierpart') - # TODO @matmair fix this test!!! - # SalesOrder = self.old_state.apps.get_model('order', 'salesorder') - - supplier = Company.objects.create( - name='Supplier A', - description='A great supplier!', - is_supplier=True, - is_customer=True, - ) - - part = Part.objects.create( - name='Bob', - description='Can we build it?', - assembly=True, - salable=True, - purchaseable=False, - tree_id=0, - level=0, - lft=0, - rght=0, - ) - supplierpart = Supplierpart.objects.create(part=part, supplier=supplier) - - # Create some orders - for ii in range(10): - order = PurchaseOrder.objects.create( - supplier=supplier, - reference=f'{ii}-abcde', - description='Just a test order', - ) - order.lines.create(part=supplierpart, quantity=12, received=1) - order.lines.create(quantity=12, received=1) - - # TODO @matmair fix this test!!! - # sales_order = SalesOrder.objects.create( - # customer=supplier, - # reference=f"{ii}-xyz", - # description="A test sales order", - # ) - # sales_order.lines.create( - # part=part, - # quantity=12, - # received=1 - # ) - - def test_po_migration(self): - """Test that the the PO lines where converted correctly.""" - PurchaseOrder = self.new_state.apps.get_model('order', 'purchaseorder') - for ii in range(10): - po = PurchaseOrder.objects.get(reference=f'{ii}-abcde') - self.assertEqual(po.extra_lines.count(), 1) - self.assertEqual(po.lines.count(), 1) - - # TODO @matmair fix this test!!! - # SalesOrder = self.new_state.apps.get_model('order', 'salesorder') - # for ii in range(10): - # so = SalesOrder.objects.get(reference=f"{ii}-xyz") - # self.assertEqual(so.extra_lines, 1) - # self.assertEqual(so.lines.count(), 1) - - -class TestShipmentDateMigration(MigratorTestCase): - """Test data migration which fixes empty 'shipment date' on SalesOrder model. - - Ref: 0105_auto_20241128_0431.py - """ - - migrate_from = ('order', '0100_remove_returnorderattachment_order_and_more') - migrate_to = ('order', '0105_auto_20241128_0431') - - def prepare(self): - """Create initial SalesOrder dataset.""" - Company = self.old_state.apps.get_model('company', 'company') - SalesOrder = self.old_state.apps.get_model('order', 'salesorder') - SalesOrderShipment = self.old_state.apps.get_model( - 'order', 'salesordershipment' - ) - - # Create a customer - customer = Company.objects.create( - name='Customer A', - description='A great customer!', - is_customer=True, - is_supplier=False, - ) - - # Create a SalesOrder (Completed, but missing shipment date) - order = SalesOrder.objects.create( - customer=customer, - reference='SO-999', - description='A test sales order', - shipment_date=None, - status=SalesOrderStatus.COMPLETE, - ) - - # Add a shipment - SalesOrderShipment.objects.create(order=order, shipment_date='2024-11-28') - - self.assertEqual(order.shipments.count(), 1) - self.assertIsNone(order.shipment_date) - - def test_migration(self): - """Test that the migration has correctly updated the SalesOrder objects.""" - SalesOrder = self.new_state.apps.get_model('order', 'salesorder') - - order = SalesOrder.objects.get(reference='SO-999') - self.assertIsNotNone(order.shipment_date) - self.assertEqual(order.shipment_date.isoformat(), '2024-11-28') diff --git a/src/backend/InvenTree/part/migrations/0003_auto_20190525_2226.py b/src/backend/InvenTree/part/migrations/0003_auto_20190525_2226.py deleted file mode 100644 index 3e805d460d..0000000000 --- a/src/backend/InvenTree/part/migrations/0003_auto_20190525_2226.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2 on 2019-05-25 12:26 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0002_auto_20190520_2204'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='has_variants', - field=models.BooleanField(default=False, help_text='Is this part a template part?'), - ), - migrations.AddField( - model_name='part', - name='variant_of', - field=models.ForeignKey(blank=True, help_text='Is this part a variant of another part?', limit_choices_to={'active': True, 'has_variants': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='variants', to='part.Part'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0003_auto_20190525_2226_squashed_0060_merge_20201112_1722.py b/src/backend/InvenTree/part/migrations/0003_auto_20190525_2226_squashed_0060_merge_20201112_1722.py new file mode 100644 index 0000000000..30fb479736 --- /dev/null +++ b/src/backend/InvenTree/part/migrations/0003_auto_20190525_2226_squashed_0060_merge_20201112_1722.py @@ -0,0 +1,448 @@ +# Generated by Django 5.2.17 on 2026-09-09 22:19 + +import InvenTree.fields +import InvenTree.validators +import django.core.validators +import django.db.models.deletion +import djmoney.models.fields +import mptt.fields +import part.models +import part.settings +import stdimage.models +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + replaces = [('part', '0003_auto_20190525_2226'), ('part', '0004_auto_20190525_2356'), ('part', '0005_auto_20190526_1119'), ('part', '0006_auto_20190526_1215'), ('part', '0007_auto_20190602_1944'), ('part', '0008_auto_20190618_0042'), ('part', '0009_part_virtual'), ('part', '0010_auto_20190620_2135'), ('part', '0011_part_revision'), ('part', '0012_auto_20190627_2144'), ('part', '0013_auto_20190628_0951'), ('part', '0014_partparameter'), ('part', '0015_auto_20190820_0251'), ('part', '0016_auto_20190820_0257'), ('part', '0017_bomitem_checksum'), ('part', '0018_auto_20190907_0941'), ('part', '0019_auto_20190908_0404'), ('part', '0020_auto_20190908_0404'), ('part', '0021_auto_20190908_0916'), ('part', '0022_auto_20190908_0918'), ('part', '0023_auto_20190913_1401'), ('part', '0024_auto_20191118_2139'), ('part', '0025_auto_20191118_2316'), ('part', '0026_auto_20200131_1022'), ('part', '0027_auto_20200202_1024'), ('part', '0028_auto_20200203_1007'), ('part', '0029_auto_20200223_0901'), ('part', '0030_auto_20200318_1027'), ('part', '0031_auto_20200318_1044'), ('part', '0032_auto_20200322_0453'), ('part', '0033_auto_20200404_0445'), ('part', '0034_auto_20200404_1238'), ('part', '0035_auto_20200406_0045'), ('part', '0036_partattachment_user'), ('part', '0037_partattachment_upload_date'), ('part', '0038_auto_20200513_0016'), ('part', '0039_auto_20200515_1127'), ('part', '0040_parttesttemplate'), ('part', '0041_auto_20200517_0348'), ('part', '0042_auto_20200518_0900'), ('part', '0043_auto_20200527_0005'), ('part', '0044_auto_20200605_0931'), ('part', '0045_auto_20200605_0932'), ('part', '0046_auto_20200804_0107'), ('part', '0047_auto_20200808_0715'), ('part', '0048_auto_20200902_1404'), ('part', '0049_partsellpricebreak'), ('part', '0050_auto_20200917_2315'), ('part', '0051_bomitem_optional'), ('part', '0052_partrelated'), ('part', '0054_auto_20201109_1246'), ('part', '0055_auto_20201110_1001'), ('part', '0056_auto_20201110_1125'), ('part', '0057_remove_partsellpricebreak_currency'), ('part', '0058_remove_partsellpricebreak_cost'), ('part', '0059_auto_20201112_1112'), ('part', '0053_partcategoryparametertemplate'), ('part', '0060_merge_20201112_1722')] + + dependencies = [ + ('common', '0004_inventreesetting'), + ('common', '0007_colortheme'), + ('part', '0002_auto_20190520_2204'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='part', + name='is_template', + field=models.BooleanField(default=False, help_text='Is this part a template part?'), + ), + migrations.AddField( + model_name='part', + name='variant_of', + field=models.ForeignKey(blank=True, help_text='Is this part a variant of another part?', limit_choices_to={'active': True, 'is_template': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='variants', to='part.part'), + ), + migrations.AddField( + model_name='part', + name='revision', + field=models.CharField(blank=True, help_text='Part rerevision code', max_length=32), + ), + migrations.AlterUniqueTogether( + name='part', + unique_together={('name', 'revision')}, + ), + migrations.RemoveField( + model_name='part', + name='variant', + ), + migrations.AlterField( + model_name='part', + name='name', + field=models.CharField(help_text='Part name (must be unique)', max_length=100, unique=True), + ), + migrations.AlterUniqueTogether( + name='part', + unique_together=set(), + ), + migrations.RemoveField( + model_name='part', + name='revision', + ), + migrations.RemoveField( + model_name='part', + name='buildable', + ), + migrations.RemoveField( + model_name='part', + name='consumable', + ), + migrations.AddField( + model_name='part', + name='assembly', + field=models.BooleanField(default=False, help_text='Can this part be built from other parts?', verbose_name='Assembly'), + ), + migrations.AddField( + model_name='part', + name='component', + field=models.BooleanField(default=True, help_text='Can this part be used to build other parts?', verbose_name='Component'), + ), + migrations.AlterField( + model_name='partcategory', + name='name', + field=models.CharField(max_length=100, validators=[InvenTree.validators.validate_tree_name]), + ), + migrations.AddField( + model_name='part', + name='virtual', + field=models.BooleanField(default=False, help_text='Is this a virtual part, such as a software product or license?'), + ), + migrations.AlterField( + model_name='part', + name='name', + field=models.CharField(help_text='Part name', max_length=100), + ), + migrations.AddField( + model_name='part', + name='revision', + field=models.CharField(blank=True, help_text='Part revision or version number', max_length=100), + ), + migrations.AddField( + model_name='bomitem', + name='reference', + field=models.CharField(blank=True, help_text='BOM item reference', max_length=500), + ), + migrations.AlterField( + model_name='bomitem', + name='note', + field=models.CharField(blank=True, help_text='BOM item notes', max_length=500), + ), + migrations.CreateModel( + name='PartParameterTemplate', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Parameter Name', max_length=100, unique=True)), + ('units', models.CharField(blank=True, help_text='Parameter Units', max_length=25)), + ], + options={ + 'verbose_name': 'Part Parameter Template', + }, + ), + migrations.CreateModel( + name='PartParameter', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('data', models.CharField(help_text='Parameter Value', max_length=500)), + ('part', models.ForeignKey(help_text='Parent Part', on_delete=django.db.models.deletion.CASCADE, related_name='parameters', to='part.part')), + ('template', models.ForeignKey(default=1, help_text='Parameter Template', on_delete=django.db.models.deletion.CASCADE, related_name='instances', to='part.partparametertemplate')), + ], + options={ + 'verbose_name': 'Part Parameter', + 'unique_together': {('part', 'template')}, + }, + ), + migrations.AddField( + model_name='bomitem', + name='checksum', + field=models.CharField(blank=True, help_text='BOM line checksum', max_length=128), + ), + migrations.AddField( + model_name='partcategory', + name='level', + field=models.PositiveIntegerField(default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name='partcategory', + name='lft', + field=models.PositiveIntegerField(default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name='partcategory', + name='rght', + field=models.PositiveIntegerField(default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name='partcategory', + name='tree_id', + field=models.PositiveIntegerField(db_index=True, default=0, editable=False), + preserve_default=False, + ), + migrations.AlterField( + model_name='part', + name='category', + field=mptt.fields.TreeForeignKey(blank=True, help_text='Part category', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='parts', to='part.partcategory'), + ), + migrations.AlterField( + model_name='part', + name='default_location', + field=mptt.fields.TreeForeignKey(blank=True, help_text='Where is this item normally stored?', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_parts', to='stock.stocklocation'), + ), + migrations.AlterField( + model_name='partcategory', + name='default_location', + field=mptt.fields.TreeForeignKey(blank=True, help_text='Default location for parts in this category', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_categories', to='stock.stocklocation'), + ), + migrations.AlterField( + model_name='partcategory', + name='parent', + field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='part.partcategory'), + ), + migrations.AlterField( + model_name='part', + name='URL', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=2000), + ), + migrations.AlterField( + model_name='bomitem', + name='quantity', + field=models.DecimalField(decimal_places=5, default=1.0, help_text='BOM quantity for this BOM item', max_digits=15, validators=[django.core.validators.MinValueValidator(0)]), + ), + migrations.AlterField( + model_name='part', + name='units', + field=models.CharField(blank=True, default='', help_text='Stock keeping units for this part', max_length=20), + ), + migrations.AlterField( + model_name='part', + name='IPN', + field=models.CharField(blank=True, help_text='Internal Part Number', max_length=100), + ), + migrations.AlterField( + model_name='part', + name='notes', + field=models.TextField(blank=True, help_text='Part notes - supports Markdown formatting'), + ), + migrations.AlterField( + model_name='bomitem', + name='part', + field=models.ForeignKey(help_text='Select parent part', limit_choices_to={'assembly': True, 'is_template': False}, on_delete=django.db.models.deletion.CASCADE, related_name='bom_items', to='part.part'), + ), + migrations.AlterField( + model_name='bomitem', + name='sub_part', + field=models.ForeignKey(help_text='Select part to be used in BOM', limit_choices_to={'component': True, 'is_template': False}, on_delete=django.db.models.deletion.CASCADE, related_name='used_in', to='part.part'), + ), + migrations.AddField( + model_name='part', + name='creation_date', + field=models.DateField(auto_now_add=True, null=True), + ), + migrations.AddField( + model_name='part', + name='creation_user', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parts_created', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='part', + name='responsible', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parts_responible', to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='partattachment', + name='attachment', + field=models.FileField(help_text='Select file to attach', upload_to='attachments'), + ), + migrations.AlterField( + model_name='part', + name='image', + field=stdimage.models.StdImageField(blank=True, force_min_size=False, null=True, upload_to=part.models.rename_part_image, variations={}), + ), + migrations.RenameField( + model_name='part', + old_name='URL', + new_name='link', + ), + migrations.AddField( + model_name='partattachment', + name='user', + field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='partattachment', + name='upload_date', + field=models.DateField(auto_now_add=True, null=True), + ), + migrations.AlterField( + model_name='partattachment', + name='comment', + field=models.CharField(blank=True, help_text='File comment', max_length=100), + ), + migrations.AddField( + model_name='part', + name='level', + field=models.PositiveIntegerField(default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name='part', + name='lft', + field=models.PositiveIntegerField(default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name='part', + name='rght', + field=models.PositiveIntegerField(default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name='part', + name='tree_id', + field=models.PositiveIntegerField(db_index=True, default=0, editable=False), + preserve_default=False, + ), + migrations.CreateModel( + name='PartTestTemplate', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('test_name', models.CharField(help_text='Enter a name for the test', max_length=100, verbose_name='Test Name')), + ('required', models.BooleanField(default=True, help_text='Is this test required to pass?', verbose_name='Required')), + ('part', models.ForeignKey(limit_choices_to={'trackable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='test_templates', to='part.part')), + ('description', models.CharField(help_text='Enter description for this test', max_length=100, null=True, verbose_name='Test Description')), + ('requires_attachment', models.BooleanField(default=False, help_text='Does this test require a file attachment when adding a test result?', verbose_name='Requires Attachment')), + ('requires_value', models.BooleanField(default=False, help_text='Does this test require a value when adding a test result?', verbose_name='Requires Value')), + ], + options={ + 'verbose_name': 'Part Test Template', + }, + ), + migrations.AlterField( + model_name='bomitem', + name='part', + field=models.ForeignKey(help_text='Select parent part', limit_choices_to={'assembly': True}, on_delete=django.db.models.deletion.CASCADE, related_name='bom_items', to='part.part'), + ), + migrations.AlterField( + model_name='bomitem', + name='sub_part', + field=models.ForeignKey(help_text='Select part to be used in BOM', limit_choices_to={'component': True}, on_delete=django.db.models.deletion.CASCADE, related_name='used_in', to='part.part'), + ), + migrations.AlterField( + model_name='partcategory', + name='name', + field=models.CharField(help_text='Name', max_length=100, validators=[InvenTree.validators.validate_tree_name]), + ), + migrations.AlterField( + model_name='partcategory', + name='description', + field=models.CharField(blank=True, help_text='Description (optional)', max_length=250), + ), + migrations.AlterField( + model_name='partcategory', + name='default_keywords', + field=models.CharField(blank=True, help_text='Default keywords for parts in this category', max_length=250, null=True), + ), + migrations.AlterModelOptions( + name='part', + options={'ordering': ['name'], 'verbose_name': 'Part', 'verbose_name_plural': 'Parts'}, + ), + migrations.AlterField( + model_name='part', + name='IPN', + field=models.CharField(blank=True, help_text='Internal Part Number', max_length=100, null=True), + ), + migrations.AlterField( + model_name='part', + name='keywords', + field=models.CharField(blank=True, help_text='Part keywords to improve visibility in search results', max_length=250, null=True), + ), + migrations.AlterField( + model_name='part', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=2000, null=True), + ), + migrations.AlterField( + model_name='part', + name='revision', + field=models.CharField(blank=True, help_text='Part revision or version number', max_length=100, null=True), + ), + migrations.AlterField( + model_name='part', + name='notes', + field=models.TextField(blank=True, help_text='Part notes - supports Markdown formatting', null=True), + ), + migrations.AlterField( + model_name='part', + name='units', + field=models.CharField(blank=True, default='', help_text='Stock keeping units for this part', max_length=20, null=True), + ), + migrations.AddField( + model_name='bomitem', + name='optional', + field=models.BooleanField(default=False, help_text='This BOM item is optional'), + ), + migrations.CreateModel( + name='PartRelated', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('part_1', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='related_parts_1', to='part.part')), + ('part_2', models.ForeignKey(help_text='Select Related Part', on_delete=django.db.models.deletion.DO_NOTHING, related_name='related_parts_2', to='part.part')), + ], + ), + migrations.AlterField( + model_name='part', + name='active', + field=models.BooleanField(default=True, help_text='Is this part active?', verbose_name='Active'), + ), + migrations.AlterField( + model_name='part', + name='component', + field=models.BooleanField(default=part.settings.part_component_default, help_text='Can this part be used to build other parts?', verbose_name='Component'), + ), + migrations.AlterField( + model_name='part', + name='purchaseable', + field=models.BooleanField(default=part.settings.part_purchaseable_default, help_text='Can this part be purchased from external suppliers?', verbose_name='Purchaseable'), + ), + migrations.AlterField( + model_name='part', + name='salable', + field=models.BooleanField(default=part.settings.part_salable_default, help_text='Can this part be sold to customers?', verbose_name='Salable'), + ), + migrations.AlterField( + model_name='part', + name='trackable', + field=models.BooleanField(default=part.settings.part_trackable_default, help_text='Does this part have tracking for unique items?', verbose_name='Trackable'), + ), + migrations.AlterField( + model_name='part', + name='virtual', + field=models.BooleanField(default=False, help_text='Is this a virtual part, such as a software product or license?', verbose_name='Virtual'), + ), + migrations.CreateModel( + name='PartSellPriceBreak', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity', InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, max_digits=15, validators=[django.core.validators.MinValueValidator(1)])), + ('cost', InvenTree.fields.RoundingDecimalField(decimal_places=5, max_digits=10, validators=[django.core.validators.MinValueValidator(0)])), + ('currency', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.currency')), + ('part', models.ForeignKey(limit_choices_to={'salable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='salepricebreaks', to='part.part')), + ('price', djmoney.models.fields.MoneyField(decimal_places=4, default_currency='', help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price')), + ('price_currency', djmoney.models.fields.CurrencyField(choices=[('AUD', 'AUD - Australian Dollar'), ('CAD', 'CAD - Canadian Dollar'), ('CNY', 'CNY - Chinese Yuan'), ('EUR', 'EUR - Euro'), ('GBP', 'GBP - British Pound'), ('JPY', 'JPY - Japanese Yen'), ('NZD', 'NZD - New Zealand Dollar'), ('USD', 'USD - US Dollar')], default='', editable=False, max_length=3)), + ], + options={ + 'verbose_name': 'Part Sale Price Break', + 'unique_together': {('part', 'quantity')}, + }, + ), + migrations.RemoveField( + model_name='partsellpricebreak', + name='currency', + ), + migrations.RemoveField( + model_name='partsellpricebreak', + name='cost', + ), + migrations.AlterField( + model_name='partsellpricebreak', + name='quantity', + field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Price break quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(1)], verbose_name='Quantity'), + ), + migrations.CreateModel( + name='PartCategoryParameterTemplate', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('default_value', models.CharField(blank=True, help_text='Default Parameter Value', max_length=500)), + ('category', models.ForeignKey(help_text='Part Category', on_delete=django.db.models.deletion.CASCADE, related_name='parameter_templates', to='part.partcategory')), + ('parameter_template', models.ForeignKey(help_text='Parameter Template', on_delete=django.db.models.deletion.CASCADE, related_name='part_categories', to='part.partparametertemplate')), + ], + options={ + 'verbose_name': 'Part Category Parameter Template', + 'constraints': [models.UniqueConstraint(fields=('category', 'parameter_template'), name='unique_category_parameter_template_pair')], + }, + ), + ] diff --git a/src/backend/InvenTree/part/migrations/0004_auto_20190525_2356.py b/src/backend/InvenTree/part/migrations/0004_auto_20190525_2356.py deleted file mode 100644 index 7b9a5fe6ac..0000000000 --- a/src/backend/InvenTree/part/migrations/0004_auto_20190525_2356.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2 on 2019-05-25 13:56 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0003_auto_20190525_2226'), - ] - - operations = [ - migrations.RenameField( - model_name='part', - old_name='has_variants', - new_name='is_template', - ), - migrations.AlterField( - model_name='part', - name='variant_of', - field=models.ForeignKey(blank=True, help_text='Is this part a variant of another part?', limit_choices_to={'active': True, 'is_template': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='variants', to='part.Part'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0005_auto_20190526_1119.py b/src/backend/InvenTree/part/migrations/0005_auto_20190526_1119.py deleted file mode 100644 index 36d55188f0..0000000000 --- a/src/backend/InvenTree/part/migrations/0005_auto_20190526_1119.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 2.2 on 2019-05-26 01:19 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0004_auto_20190525_2356'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='revision', - field=models.CharField(blank=True, help_text='Part rerevision code', max_length=32), - ), - migrations.AlterUniqueTogether( - name='part', - unique_together={('name', 'revision')}, - ), - migrations.RemoveField( - model_name='part', - name='variant', - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0006_auto_20190526_1215.py b/src/backend/InvenTree/part/migrations/0006_auto_20190526_1215.py deleted file mode 100644 index 1fbf02407f..0000000000 --- a/src/backend/InvenTree/part/migrations/0006_auto_20190526_1215.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 2.2 on 2019-05-26 02:15 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0005_auto_20190526_1119'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='name', - field=models.CharField(help_text='Part name (must be unique)', max_length=100, unique=True), - ), - migrations.AlterUniqueTogether( - name='part', - unique_together=set(), - ), - migrations.RemoveField( - model_name='part', - name='revision', - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0007_auto_20190602_1944.py b/src/backend/InvenTree/part/migrations/0007_auto_20190602_1944.py deleted file mode 100644 index 9bfeb7fb8d..0000000000 --- a/src/backend/InvenTree/part/migrations/0007_auto_20190602_1944.py +++ /dev/null @@ -1,42 +0,0 @@ -# Generated by Django 2.2 on 2019-06-02 09:44 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0006_auto_20190526_1215'), - ] - - operations = [ - migrations.RemoveField( - model_name='part', - name='buildable', - ), - migrations.RemoveField( - model_name='part', - name='consumable', - ), - migrations.AddField( - model_name='part', - name='assembly', - field=models.BooleanField(default=False, help_text='Can this part be built from other parts?', verbose_name='Assembly'), - ), - migrations.AddField( - model_name='part', - name='component', - field=models.BooleanField(default=True, help_text='Can this part be used to build other parts?', verbose_name='Component'), - ), - migrations.AlterField( - model_name='bomitem', - name='part', - field=models.ForeignKey(help_text='Select parent part', limit_choices_to={'active': True, 'assembly': True}, on_delete=django.db.models.deletion.CASCADE, related_name='bom_items', to='part.Part'), - ), - migrations.AlterField( - model_name='bomitem', - name='sub_part', - field=models.ForeignKey(help_text='Select part to be used in BOM', limit_choices_to={'active': True, 'component': True}, on_delete=django.db.models.deletion.CASCADE, related_name='used_in', to='part.Part'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0008_auto_20190618_0042.py b/src/backend/InvenTree/part/migrations/0008_auto_20190618_0042.py deleted file mode 100644 index c9e0699f9b..0000000000 --- a/src/backend/InvenTree/part/migrations/0008_auto_20190618_0042.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.2 on 2019-06-17 14:42 - -import InvenTree.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0007_auto_20190602_1944'), - ] - - operations = [ - migrations.AlterField( - model_name='partcategory', - name='name', - field=models.CharField(max_length=100, unique=True, validators=[InvenTree.validators.validate_tree_name]), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0009_part_virtual.py b/src/backend/InvenTree/part/migrations/0009_part_virtual.py deleted file mode 100644 index 8a934e9064..0000000000 --- a/src/backend/InvenTree/part/migrations/0009_part_virtual.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.2 on 2019-06-18 08:33 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0008_auto_20190618_0042'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='virtual', - field=models.BooleanField(default=False, help_text='Is this a virtual part, such as a software product or license?'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0010_auto_20190620_2135.py b/src/backend/InvenTree/part/migrations/0010_auto_20190620_2135.py deleted file mode 100644 index a8bd18c43a..0000000000 --- a/src/backend/InvenTree/part/migrations/0010_auto_20190620_2135.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.2 on 2019-06-20 11:35 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0009_part_virtual'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='name', - field=models.CharField(help_text='Part name', max_length=100), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0011_part_revision.py b/src/backend/InvenTree/part/migrations/0011_part_revision.py deleted file mode 100644 index 2ae0aa6b19..0000000000 --- a/src/backend/InvenTree/part/migrations/0011_part_revision.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.2 on 2019-06-20 11:37 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0010_auto_20190620_2135'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='revision', - field=models.CharField(blank=True, help_text='Part revision or version number', max_length=100), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0012_auto_20190627_2144.py b/src/backend/InvenTree/part/migrations/0012_auto_20190627_2144.py deleted file mode 100644 index ffd574b61d..0000000000 --- a/src/backend/InvenTree/part/migrations/0012_auto_20190627_2144.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 2.2.2 on 2019-06-27 11:44 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0011_part_revision'), - ] - - operations = [ - migrations.AddField( - model_name='bomitem', - name='reference', - field=models.CharField(blank=True, help_text='BOM item reference', max_length=500), - ), - migrations.AlterField( - model_name='bomitem', - name='note', - field=models.CharField(blank=True, help_text='BOM item notes', max_length=500), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0013_auto_20190628_0951.py b/src/backend/InvenTree/part/migrations/0013_auto_20190628_0951.py deleted file mode 100644 index df9f8fdb14..0000000000 --- a/src/backend/InvenTree/part/migrations/0013_auto_20190628_0951.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2.2 on 2019-06-27 23:51 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0012_auto_20190627_2144'), - ] - - operations = [ - migrations.AlterField( - model_name='bomitem', - name='part', - field=models.ForeignKey(help_text='Select parent part', limit_choices_to={'assembly': True}, on_delete=django.db.models.deletion.CASCADE, related_name='bom_items', to='part.Part'), - ), - migrations.AlterField( - model_name='bomitem', - name='sub_part', - field=models.ForeignKey(help_text='Select part to be used in BOM', limit_choices_to={'component': True}, on_delete=django.db.models.deletion.CASCADE, related_name='used_in', to='part.Part'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0014_partparameter.py b/src/backend/InvenTree/part/migrations/0014_partparameter.py deleted file mode 100644 index e9058bbdf6..0000000000 --- a/src/backend/InvenTree/part/migrations/0014_partparameter.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 2.2.4 on 2019-08-20 02:30 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0013_auto_20190628_0951'), - ] - - operations = [ - migrations.CreateModel( - name='PartParameter', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(help_text='Parameter Name', max_length=100)), - ('data', models.CharField(help_text='Parameter Value', max_length=100)), - ('part', models.ForeignKey(help_text='Parent Part', on_delete=django.db.models.deletion.CASCADE, related_name='parameters', to='part.Part')), - ], - options={ - 'verbose_name': 'Part Parameter', - }, - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0015_auto_20190820_0251.py b/src/backend/InvenTree/part/migrations/0015_auto_20190820_0251.py deleted file mode 100644 index a05555beb5..0000000000 --- a/src/backend/InvenTree/part/migrations/0015_auto_20190820_0251.py +++ /dev/null @@ -1,40 +0,0 @@ -# Generated by Django 2.2.4 on 2019-08-20 02:51 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0014_partparameter'), - ] - - operations = [ - migrations.CreateModel( - name='PartParameterTemplate', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(help_text='Parameter Name', max_length=100)), - ('units', models.CharField(blank=True, help_text='Parameter Units', max_length=25)), - ], - options={ - 'verbose_name': 'Part Parameter Template', - }, - ), - migrations.RemoveField( - model_name='partparameter', - name='name', - ), - migrations.AlterField( - model_name='partparameter', - name='data', - field=models.CharField(help_text='Parameter Value', max_length=500), - ), - migrations.AddField( - model_name='partparameter', - name='template', - field=models.ForeignKey(default=1, help_text='Parameter Template', on_delete=django.db.models.deletion.CASCADE, related_name='instances', to='part.PartParameterTemplate'), - preserve_default=False, - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0016_auto_20190820_0257.py b/src/backend/InvenTree/part/migrations/0016_auto_20190820_0257.py deleted file mode 100644 index 0399129078..0000000000 --- a/src/backend/InvenTree/part/migrations/0016_auto_20190820_0257.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.2.4 on 2019-08-20 02:57 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0015_auto_20190820_0251'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='partparameter', - unique_together={('part', 'template')}, - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0017_bomitem_checksum.py b/src/backend/InvenTree/part/migrations/0017_bomitem_checksum.py deleted file mode 100644 index 3dfaae7a09..0000000000 --- a/src/backend/InvenTree/part/migrations/0017_bomitem_checksum.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.4 on 2019-09-05 02:57 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0016_auto_20190820_0257'), - ] - - operations = [ - migrations.AddField( - model_name='bomitem', - name='checksum', - field=models.CharField(blank=True, help_text='BOM line checksum', max_length=128), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0018_auto_20190907_0941.py b/src/backend/InvenTree/part/migrations/0018_auto_20190907_0941.py deleted file mode 100644 index 539759146b..0000000000 --- a/src/backend/InvenTree/part/migrations/0018_auto_20190907_0941.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.4 on 2019-09-07 09:41 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0017_bomitem_checksum'), - ] - - operations = [ - migrations.AlterField( - model_name='partparametertemplate', - name='name', - field=models.CharField(help_text='Parameter Name', max_length=100, unique=True), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0019_auto_20190908_0404.py b/src/backend/InvenTree/part/migrations/0019_auto_20190908_0404.py deleted file mode 100644 index 160bb929b9..0000000000 --- a/src/backend/InvenTree/part/migrations/0019_auto_20190908_0404.py +++ /dev/null @@ -1,37 +0,0 @@ -# Generated by Django 2.2.5 on 2019-09-08 04:04 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0018_auto_20190907_0941'), - ] - - operations = [ - migrations.AddField( - model_name='partcategory', - name='level', - field=models.PositiveIntegerField(default=0, editable=False), - preserve_default=False, - ), - migrations.AddField( - model_name='partcategory', - name='lft', - field=models.PositiveIntegerField(default=0, editable=False), - preserve_default=False, - ), - migrations.AddField( - model_name='partcategory', - name='rght', - field=models.PositiveIntegerField(default=0, editable=False), - preserve_default=False, - ), - migrations.AddField( - model_name='partcategory', - name='tree_id', - field=models.PositiveIntegerField(db_index=True, default=0, editable=False), - preserve_default=False, - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0020_auto_20190908_0404.py b/src/backend/InvenTree/part/migrations/0020_auto_20190908_0404.py deleted file mode 100644 index 725ce67305..0000000000 --- a/src/backend/InvenTree/part/migrations/0020_auto_20190908_0404.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 2.2.5 on 2019-09-08 04:04 - -from django.db import migrations - - -class Migration(migrations.Migration): - - atomic = False - - dependencies = [ - ('part', '0019_auto_20190908_0404'), - ] - - operations = [] diff --git a/src/backend/InvenTree/part/migrations/0021_auto_20190908_0916.py b/src/backend/InvenTree/part/migrations/0021_auto_20190908_0916.py deleted file mode 100644 index 0427487246..0000000000 --- a/src/backend/InvenTree/part/migrations/0021_auto_20190908_0916.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 2.2.5 on 2019-09-08 09:16 - -from django.db import migrations -import django.db.models.deletion -import mptt.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0020_auto_20190908_0404'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='category', - field=mptt.fields.TreeForeignKey(blank=True, help_text='Part category', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='parts', to='part.PartCategory'), - ), - migrations.AlterField( - model_name='part', - name='default_location', - field=mptt.fields.TreeForeignKey(blank=True, help_text='Where is this item normally stored?', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_parts', to='stock.StockLocation'), - ), - migrations.AlterField( - model_name='partcategory', - name='default_location', - field=mptt.fields.TreeForeignKey(blank=True, help_text='Default location for parts in this category', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_categories', to='stock.StockLocation'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0022_auto_20190908_0918.py b/src/backend/InvenTree/part/migrations/0022_auto_20190908_0918.py deleted file mode 100644 index 48782ed545..0000000000 --- a/src/backend/InvenTree/part/migrations/0022_auto_20190908_0918.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2.5 on 2019-09-08 09:18 - -from django.db import migrations -import django.db.models.deletion -import mptt.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0021_auto_20190908_0916'), - ] - - operations = [ - migrations.AlterField( - model_name='partcategory', - name='parent', - field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='part.PartCategory'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0023_auto_20190913_1401.py b/src/backend/InvenTree/part/migrations/0023_auto_20190913_1401.py deleted file mode 100644 index c3b5619b55..0000000000 --- a/src/backend/InvenTree/part/migrations/0023_auto_20190913_1401.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.5 on 2019-09-13 14:01 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0022_auto_20190908_0918'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='URL', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0024_auto_20191118_2139.py b/src/backend/InvenTree/part/migrations/0024_auto_20191118_2139.py deleted file mode 100644 index 95892993b2..0000000000 --- a/src/backend/InvenTree/part/migrations/0024_auto_20191118_2139.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.5 on 2019-11-18 21:39 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0023_auto_20190913_1401'), - ] - - operations = [ - migrations.AlterField( - model_name='bomitem', - name='quantity', - field=models.DecimalField(decimal_places=5, default=1.0, help_text='BOM quantity for this BOM item', max_digits=15, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0025_auto_20191118_2316.py b/src/backend/InvenTree/part/migrations/0025_auto_20191118_2316.py deleted file mode 100644 index cbdd7be75a..0000000000 --- a/src/backend/InvenTree/part/migrations/0025_auto_20191118_2316.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.5 on 2019-11-18 23:16 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0024_auto_20191118_2139'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='units', - field=models.CharField(blank=True, default='', help_text='Stock keeping units for this part', max_length=20), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0026_auto_20200131_1022.py b/src/backend/InvenTree/part/migrations/0026_auto_20200131_1022.py deleted file mode 100644 index c0f1ead30b..0000000000 --- a/src/backend/InvenTree/part/migrations/0026_auto_20200131_1022.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.9 on 2020-01-31 10:22 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0025_auto_20191118_2316'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='notes', - field=models.TextField(help_text='Part notes - supports Markdown formatting'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0027_auto_20200202_1024.py b/src/backend/InvenTree/part/migrations/0027_auto_20200202_1024.py deleted file mode 100644 index 61def4af5e..0000000000 --- a/src/backend/InvenTree/part/migrations/0027_auto_20200202_1024.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.9 on 2020-02-02 10:24 - -import InvenTree.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0026_auto_20200131_1022'), - ] - - operations = [ - migrations.AlterField( - model_name='partcategory', - name='name', - field=models.CharField(max_length=100, validators=[InvenTree.validators.validate_tree_name]), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0028_auto_20200203_1007.py b/src/backend/InvenTree/part/migrations/0028_auto_20200203_1007.py deleted file mode 100644 index b34715ecbc..0000000000 --- a/src/backend/InvenTree/part/migrations/0028_auto_20200203_1007.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.9 on 2020-02-03 10:07 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0027_auto_20200202_1024'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='IPN', - field=models.CharField(blank=True, help_text='Internal Part Number', max_length=100), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0029_auto_20200223_0901.py b/src/backend/InvenTree/part/migrations/0029_auto_20200223_0901.py deleted file mode 100644 index c0db32fb60..0000000000 --- a/src/backend/InvenTree/part/migrations/0029_auto_20200223_0901.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.9 on 2020-02-23 09:01 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0028_auto_20200203_1007'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='notes', - field=models.TextField(blank=True, help_text='Part notes - supports Markdown formatting'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0030_auto_20200318_1027.py b/src/backend/InvenTree/part/migrations/0030_auto_20200318_1027.py deleted file mode 100644 index 0c61e9717d..0000000000 --- a/src/backend/InvenTree/part/migrations/0030_auto_20200318_1027.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2.9 on 2020-03-18 10:27 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0029_auto_20200223_0901'), - ] - - operations = [ - migrations.AlterField( - model_name='bomitem', - name='part', - field=models.ForeignKey(help_text='Select parent part', limit_choices_to={'assembly': True, 'is_template': False}, on_delete=django.db.models.deletion.CASCADE, related_name='bom_items', to='part.Part'), - ), - migrations.AlterField( - model_name='bomitem', - name='sub_part', - field=models.ForeignKey(help_text='Select part to be used in BOM', limit_choices_to={'component': True, 'is_template': False}, on_delete=django.db.models.deletion.CASCADE, related_name='used_in', to='part.Part'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0031_auto_20200318_1044.py b/src/backend/InvenTree/part/migrations/0031_auto_20200318_1044.py deleted file mode 100644 index c43af364f2..0000000000 --- a/src/backend/InvenTree/part/migrations/0031_auto_20200318_1044.py +++ /dev/null @@ -1,31 +0,0 @@ -# Generated by Django 2.2.9 on 2020-03-18 10:44 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('part', '0030_auto_20200318_1027'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='creation_date', - field=models.DateField(auto_now_add=True, null=True), - ), - migrations.AddField( - model_name='part', - name='creation_user', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parts_created', to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='part', - name='responsible', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parts_responible', to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0032_auto_20200322_0453.py b/src/backend/InvenTree/part/migrations/0032_auto_20200322_0453.py deleted file mode 100644 index 6b1403b0be..0000000000 --- a/src/backend/InvenTree/part/migrations/0032_auto_20200322_0453.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.9 on 2020-03-22 04:53 - -import InvenTree.models -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0031_auto_20200318_1044'), - ] - - operations = [ - migrations.AlterField( - model_name='partattachment', - name='attachment', - field=models.FileField(help_text='Select file to attach', upload_to='attachments'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0033_auto_20200404_0445.py b/src/backend/InvenTree/part/migrations/0033_auto_20200404_0445.py deleted file mode 100644 index 4c2b8c1c96..0000000000 --- a/src/backend/InvenTree/part/migrations/0033_auto_20200404_0445.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2.10 on 2020-04-04 04:45 - -from django.db import migrations -import part.models -import stdimage.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0032_auto_20200322_0453'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='image', - field=stdimage.models.StdImageField(blank=True, null=True, upload_to=part.models.rename_part_image), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0034_auto_20200404_1238.py b/src/backend/InvenTree/part/migrations/0034_auto_20200404_1238.py deleted file mode 100644 index a720fffa1e..0000000000 --- a/src/backend/InvenTree/part/migrations/0034_auto_20200404_1238.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 2.2.10 on 2020-04-04 12:38 - -from django.db import migrations - - -def create_thumbnails(apps, schema_editor): - """ - Create thumbnails for all existing Part images. - - Note: This functionality is now performed in apps.py, - as running the thumbnail script here caused too many database level errors. - - This migration is left here to maintain the database migration history - - """ - pass - - -class Migration(migrations.Migration): - - atomic = False - - dependencies = [ - ('part', '0033_auto_20200404_0445'), - ] - - operations = [ - migrations.RunPython(create_thumbnails, reverse_code=create_thumbnails), - ] diff --git a/src/backend/InvenTree/part/migrations/0035_auto_20200406_0045.py b/src/backend/InvenTree/part/migrations/0035_auto_20200406_0045.py deleted file mode 100644 index 92f0472880..0000000000 --- a/src/backend/InvenTree/part/migrations/0035_auto_20200406_0045.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.10 on 2020-04-06 00:45 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0034_auto_20200404_1238'), - ] - - operations = [ - migrations.RenameField( - model_name='part', - old_name='URL', - new_name='link', - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0036_partattachment_user.py b/src/backend/InvenTree/part/migrations/0036_partattachment_user.py deleted file mode 100644 index d59bc7ffb2..0000000000 --- a/src/backend/InvenTree/part/migrations/0036_partattachment_user.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.0.5 on 2020-05-12 10:33 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('part', '0035_auto_20200406_0045'), - ] - - operations = [ - migrations.AddField( - model_name='partattachment', - name='user', - field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0037_partattachment_upload_date.py b/src/backend/InvenTree/part/migrations/0037_partattachment_upload_date.py deleted file mode 100644 index 4169870178..0000000000 --- a/src/backend/InvenTree/part/migrations/0037_partattachment_upload_date.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.5 on 2020-05-12 10:54 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0036_partattachment_user'), - ] - - operations = [ - migrations.AddField( - model_name='partattachment', - name='upload_date', - field=models.DateField(auto_now_add=True, null=True), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0038_auto_20200513_0016.py b/src/backend/InvenTree/part/migrations/0038_auto_20200513_0016.py deleted file mode 100644 index a472480186..0000000000 --- a/src/backend/InvenTree/part/migrations/0038_auto_20200513_0016.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.5 on 2020-05-13 00:16 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0037_partattachment_upload_date'), - ] - - operations = [ - migrations.AlterField( - model_name='partattachment', - name='comment', - field=models.CharField(blank=True, help_text='File comment', max_length=100), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0039_auto_20200515_1127.py b/src/backend/InvenTree/part/migrations/0039_auto_20200515_1127.py deleted file mode 100644 index a2a83d093c..0000000000 --- a/src/backend/InvenTree/part/migrations/0039_auto_20200515_1127.py +++ /dev/null @@ -1,39 +0,0 @@ -# Generated by Django 3.0.5 on 2020-05-15 11:27 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - atomic = False - - dependencies = [ - ('part', '0038_auto_20200513_0016'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='level', - field=models.PositiveIntegerField(default=0, editable=False), - preserve_default=False, - ), - migrations.AddField( - model_name='part', - name='lft', - field=models.PositiveIntegerField(default=0, editable=False), - preserve_default=False, - ), - migrations.AddField( - model_name='part', - name='rght', - field=models.PositiveIntegerField(default=0, editable=False), - preserve_default=False, - ), - migrations.AddField( - model_name='part', - name='tree_id', - field=models.PositiveIntegerField(db_index=True, default=0, editable=False), - preserve_default=False, - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0040_parttesttemplate.py b/src/backend/InvenTree/part/migrations/0040_parttesttemplate.py deleted file mode 100644 index 4471cf19c5..0000000000 --- a/src/backend/InvenTree/part/migrations/0040_parttesttemplate.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 3.0.5 on 2020-05-17 03:26 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0039_auto_20200515_1127'), - ] - - operations = [ - migrations.CreateModel( - name='PartTestTemplate', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('test_name', models.CharField(help_text='Enter a name for the test', max_length=100, verbose_name='Test name')), - ('required', models.BooleanField(default=True, help_text='Is this test required to pass?', verbose_name='Required')), - ('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='test_templates', to='part.Part')), - ], - options={ - 'verbose_name': 'Part Test Template', - }, - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0041_auto_20200517_0348.py b/src/backend/InvenTree/part/migrations/0041_auto_20200517_0348.py deleted file mode 100644 index ff24313193..0000000000 --- a/src/backend/InvenTree/part/migrations/0041_auto_20200517_0348.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.0.5 on 2020-05-17 03:48 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0040_parttesttemplate'), - ] - - operations = [ - migrations.AlterField( - model_name='parttesttemplate', - name='part', - field=models.ForeignKey(limit_choices_to={'trackable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='test_templates', to='part.Part'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0042_auto_20200518_0900.py b/src/backend/InvenTree/part/migrations/0042_auto_20200518_0900.py deleted file mode 100644 index 30b1734472..0000000000 --- a/src/backend/InvenTree/part/migrations/0042_auto_20200518_0900.py +++ /dev/null @@ -1,33 +0,0 @@ -# Generated by Django 3.0.5 on 2020-05-18 09:00 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0041_auto_20200517_0348'), - ] - - operations = [ - migrations.AddField( - model_name='parttesttemplate', - name='description', - field=models.CharField(help_text='Enter description for this test', max_length=100, null=True, verbose_name='Test Description'), - ), - migrations.AddField( - model_name='parttesttemplate', - name='requires_attachment', - field=models.BooleanField(default=False, help_text='Does this test require a file attachment when adding a test result?', verbose_name='Requires Attachment'), - ), - migrations.AddField( - model_name='parttesttemplate', - name='requires_value', - field=models.BooleanField(default=False, help_text='Does this test require a value when adding a test result?', verbose_name='Requires Value'), - ), - migrations.AlterField( - model_name='parttesttemplate', - name='test_name', - field=models.CharField(help_text='Enter a name for the test', max_length=100, verbose_name='Test Name'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0043_auto_20200527_0005.py b/src/backend/InvenTree/part/migrations/0043_auto_20200527_0005.py deleted file mode 100644 index 72d1c1ddc4..0000000000 --- a/src/backend/InvenTree/part/migrations/0043_auto_20200527_0005.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.0.5 on 2020-05-27 00:05 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0042_auto_20200518_0900'), - ] - - operations = [ - migrations.AlterField( - model_name='bomitem', - name='part', - field=models.ForeignKey(help_text='Select parent part', limit_choices_to={'assembly': True}, on_delete=django.db.models.deletion.CASCADE, related_name='bom_items', to='part.Part'), - ), - migrations.AlterField( - model_name='bomitem', - name='sub_part', - field=models.ForeignKey(help_text='Select part to be used in BOM', limit_choices_to={'component': True}, on_delete=django.db.models.deletion.CASCADE, related_name='used_in', to='part.Part'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0044_auto_20200605_0931.py b/src/backend/InvenTree/part/migrations/0044_auto_20200605_0931.py deleted file mode 100644 index bdce0ebc02..0000000000 --- a/src/backend/InvenTree/part/migrations/0044_auto_20200605_0931.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.0.5 on 2020-06-05 09:31 - -import InvenTree.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0043_auto_20200527_0005'), - ] - - operations = [ - migrations.AlterField( - model_name='partcategory', - name='description', - field=models.CharField(blank=True, help_text='Description', max_length=250), - ), - migrations.AlterField( - model_name='partcategory', - name='name', - field=models.CharField(help_text='Name', max_length=100, validators=[InvenTree.validators.validate_tree_name]), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0045_auto_20200605_0932.py b/src/backend/InvenTree/part/migrations/0045_auto_20200605_0932.py deleted file mode 100644 index a9be054468..0000000000 --- a/src/backend/InvenTree/part/migrations/0045_auto_20200605_0932.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.5 on 2020-06-05 09:32 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0044_auto_20200605_0931'), - ] - - operations = [ - migrations.AlterField( - model_name='partcategory', - name='description', - field=models.CharField(blank=True, help_text='Description (optional)', max_length=250), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0046_auto_20200804_0107.py b/src/backend/InvenTree/part/migrations/0046_auto_20200804_0107.py deleted file mode 100644 index 5a0952c1e4..0000000000 --- a/src/backend/InvenTree/part/migrations/0046_auto_20200804_0107.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.7 on 2020-08-04 01:07 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0045_auto_20200605_0932'), - ] - - operations = [ - migrations.AlterField( - model_name='partcategory', - name='default_keywords', - field=models.CharField(blank=True, help_text='Default keywords for parts in this category', max_length=250, null=True), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0047_auto_20200808_0715.py b/src/backend/InvenTree/part/migrations/0047_auto_20200808_0715.py deleted file mode 100644 index 4fc3d5a7d9..0000000000 --- a/src/backend/InvenTree/part/migrations/0047_auto_20200808_0715.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.0.7 on 2020-08-08 07:15 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0046_auto_20200804_0107'), - ] - - operations = [ - migrations.AlterModelOptions( - name='part', - options={'ordering': ['name'], 'verbose_name': 'Part', 'verbose_name_plural': 'Parts'}, - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0048_auto_20200902_1404.py b/src/backend/InvenTree/part/migrations/0048_auto_20200902_1404.py deleted file mode 100644 index bbb6547c48..0000000000 --- a/src/backend/InvenTree/part/migrations/0048_auto_20200902_1404.py +++ /dev/null @@ -1,45 +0,0 @@ -# Generated by Django 3.0.7 on 2020-09-02 14:04 - -import InvenTree.fields - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0047_auto_20200808_0715'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='IPN', - field=models.CharField(blank=True, help_text='Internal Part Number', max_length=100, null=True), - ), - migrations.AlterField( - model_name='part', - name='keywords', - field=models.CharField(blank=True, help_text='Part keywords to improve visibility in search results', max_length=250, null=True), - ), - migrations.AlterField( - model_name='part', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', null=True), - ), - migrations.AlterField( - model_name='part', - name='revision', - field=models.CharField(blank=True, help_text='Part revision or version number', max_length=100, null=True), - ), - migrations.AlterField( - model_name='part', - name='notes', - field=models.TextField(blank=True, help_text='Part notes - supports Markdown formatting', null=True), - ), - migrations.AlterField( - model_name='part', - name='units', - field=models.CharField(blank=True, default='', help_text='Stock keeping units for this part', max_length=20, null=True), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0049_partsellpricebreak.py b/src/backend/InvenTree/part/migrations/0049_partsellpricebreak.py deleted file mode 100644 index 8332d353af..0000000000 --- a/src/backend/InvenTree/part/migrations/0049_partsellpricebreak.py +++ /dev/null @@ -1,31 +0,0 @@ -# Generated by Django 3.0.7 on 2020-09-17 13:22 - -import InvenTree.fields -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0007_colortheme'), - ('part', '0048_auto_20200902_1404'), - ] - - operations = [ - migrations.CreateModel( - name='PartSellPriceBreak', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('quantity', InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, max_digits=15, validators=[django.core.validators.MinValueValidator(1)])), - ('cost', InvenTree.fields.RoundingDecimalField(decimal_places=5, max_digits=10, validators=[django.core.validators.MinValueValidator(0)])), - ('currency', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.Currency')), - ('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='salepricebreaks', to='part.Part')), - ], - options={ - 'verbose_name': 'Part Sale Price Break', - 'unique_together': {('part', 'quantity')}, - }, - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0050_auto_20200917_2315.py b/src/backend/InvenTree/part/migrations/0050_auto_20200917_2315.py deleted file mode 100644 index 635294093d..0000000000 --- a/src/backend/InvenTree/part/migrations/0050_auto_20200917_2315.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.0.7 on 2020-09-17 23:15 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0049_partsellpricebreak'), - ] - - operations = [ - migrations.AlterField( - model_name='partsellpricebreak', - name='part', - field=models.ForeignKey(limit_choices_to={'salable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='salepricebreaks', to='part.Part'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0051_bomitem_optional.py b/src/backend/InvenTree/part/migrations/0051_bomitem_optional.py deleted file mode 100644 index 04920b3c10..0000000000 --- a/src/backend/InvenTree/part/migrations/0051_bomitem_optional.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.7 on 2020-10-04 13:34 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0050_auto_20200917_2315'), - ] - - operations = [ - migrations.AddField( - model_name='bomitem', - name='optional', - field=models.BooleanField(default=False, help_text='This BOM item is optional'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0052_partrelated.py b/src/backend/InvenTree/part/migrations/0052_partrelated.py deleted file mode 100644 index 7f9dc896a5..0000000000 --- a/src/backend/InvenTree/part/migrations/0052_partrelated.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 3.0.7 on 2020-10-16 20:42 - -from django.db import migrations, models -import django.db.models.deletion -import InvenTree.fields - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0051_bomitem_optional'), - ] - - operations = [ - migrations.CreateModel( - name='PartRelated', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('part_1', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='related_parts_1', to='part.Part')), - ('part_2', models.ForeignKey(help_text='Select Related Part', on_delete=django.db.models.deletion.DO_NOTHING, related_name='related_parts_2', to='part.Part')), - ], - ), - migrations.AlterField( - model_name='part', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', null=True), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0053_partcategoryparametertemplate.py b/src/backend/InvenTree/part/migrations/0053_partcategoryparametertemplate.py deleted file mode 100644 index e6742dd6c8..0000000000 --- a/src/backend/InvenTree/part/migrations/0053_partcategoryparametertemplate.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 3.0.7 on 2020-10-30 18:04 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0052_partrelated'), - ] - - operations = [ - migrations.CreateModel( - name='PartCategoryParameterTemplate', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('default_value', models.CharField(blank=True, help_text='Default Parameter Value', max_length=500)), - ('category', models.ForeignKey(help_text='Part Category', on_delete=django.db.models.deletion.CASCADE, related_name='parameter_templates', to='part.PartCategory')), - ('parameter_template', models.ForeignKey(help_text='Parameter Template', on_delete=django.db.models.deletion.CASCADE, related_name='part_categories', to='part.PartParameterTemplate')), - ], - options={ - 'verbose_name': 'Part Category Parameter Template', - }, - ), - migrations.AddConstraint( - model_name='partcategoryparametertemplate', - constraint=models.UniqueConstraint(fields=('category', 'parameter_template'), name='unique_category_parameter_template_pair'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0054_auto_20201109_1246.py b/src/backend/InvenTree/part/migrations/0054_auto_20201109_1246.py deleted file mode 100644 index 5a27c70506..0000000000 --- a/src/backend/InvenTree/part/migrations/0054_auto_20201109_1246.py +++ /dev/null @@ -1,44 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-09 12:46 - -from django.db import migrations, models -import part.settings - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0052_partrelated'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='active', - field=models.BooleanField(default=True, help_text='Is this part active?', verbose_name='Active'), - ), - migrations.AlterField( - model_name='part', - name='component', - field=models.BooleanField(default=part.settings.part_component_default, help_text='Can this part be used to build other parts?', verbose_name='Component'), - ), - migrations.AlterField( - model_name='part', - name='purchaseable', - field=models.BooleanField(default=part.settings.part_purchaseable_default, help_text='Can this part be purchased from external suppliers?', verbose_name='Purchaseable'), - ), - migrations.AlterField( - model_name='part', - name='salable', - field=models.BooleanField(default=part.settings.part_salable_default, help_text='Can this part be sold to customers?', verbose_name='Salable'), - ), - migrations.AlterField( - model_name='part', - name='trackable', - field=models.BooleanField(default=part.settings.part_trackable_default, help_text='Does this part have tracking for unique items?', verbose_name='Trackable'), - ), - migrations.AlterField( - model_name='part', - name='virtual', - field=models.BooleanField(default=False, help_text='Is this a virtual part, such as a software product or license?', verbose_name='Virtual'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0055_auto_20201110_1001.py b/src/backend/InvenTree/part/migrations/0055_auto_20201110_1001.py deleted file mode 100644 index a118b1da16..0000000000 --- a/src/backend/InvenTree/part/migrations/0055_auto_20201110_1001.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-10 10:01 - -from django.db import migrations -import djmoney.models.fields -import common.currency - - -class Migration(migrations.Migration): - - dependencies = [ - ('common', '0004_inventreesetting'), - ('part', '0054_auto_20201109_1246'), - ] - - operations = [ - migrations.AddField( - model_name='partsellpricebreak', - name='price', - field=djmoney.models.fields.MoneyField(decimal_places=4, default_currency='', help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price'), - ), - migrations.AddField( - model_name='partsellpricebreak', - name='price_currency', - field=djmoney.models.fields.CurrencyField(choices=common.currency.currency_code_mappings(), default='', editable=False, max_length=3), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0056_auto_20201110_1125.py b/src/backend/InvenTree/part/migrations/0056_auto_20201110_1125.py deleted file mode 100644 index f3d0faa26b..0000000000 --- a/src/backend/InvenTree/part/migrations/0056_auto_20201110_1125.py +++ /dev/null @@ -1,155 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-10 11:25 - -import logging - -from django.db import migrations - -from moneyed import CURRENCIES -from django.db import migrations, connection -from company.models import SupplierPriceBreak - - -logger = logging.getLogger('inventree') - - -def migrate_currencies(apps, schema_editor): - """ - Migrate from the 'old' method of handling currencies, - to the new method which uses the django-money library. - - Previously, we created a custom Currency model, - which was very simplistic. - - Here we will attempt to map each existing "currency" reference - for the SupplierPriceBreak model, to a new django-money compatible currency. - """ - - logger.debug("Updating currency references for SupplierPriceBreak model...") - - # A list of available currency codes - currency_codes = CURRENCIES.keys() - - cursor = connection.cursor() - - # The 'suffix' field denotes the currency code - response = cursor.execute('SELECT id, suffix, description from common_currency;') - - results = cursor.fetchall() - - remap = {} - - for index, row in enumerate(results): # pragma: no cover - pk, suffix, description = row - - suffix = suffix.strip().upper() - - if suffix not in currency_codes: - print("Missing suffix:", suffix) - - while suffix not in currency_codes: - # Ask the user to input a valid currency - print(f"Could not find a valid currency matching '{suffix}'.") - print("Please enter a valid currency code") - suffix = str(input("> ")).strip() - - if pk not in remap.keys(): - remap[pk] = suffix - - # Now iterate through each PartSellPriceBreak and update the rows - response = cursor.execute('SELECT id, cost, currency_id, price, price_currency from part_partsellpricebreak;') - - results = cursor.fetchall() - - count = 0 - - for index, row in enumerate(results): # pragma: no cover - pk, cost, currency_id, price, price_currency = row - - # Copy the 'cost' field across to the 'price' field - response = cursor.execute(f'UPDATE part_partsellpricebreak set price={cost} where id={pk};') - - # Extract the updated currency code - currency_code = remap.get(currency_id, 'USD') - - # Update the currency code - response = cursor.execute(f"UPDATE part_partsellpricebreak set price_currency='{currency_code}' where id={pk};") - - count += 1 - - if count > 0: # pragma: no cover - print(f"Updated {count} SupplierPriceBreak rows") - -def reverse_currencies(apps, schema_editor): # pragma: no cover - """ - Reverse the "update" process. - - Here we may be in the situation that the legacy "Currency" table is empty, - and so we have to re-populate it based on the new price_currency codes. - """ - - print("Reversing currency migration...") - - cursor = connection.cursor() - - # Extract a list of currency codes which are in use - response = cursor.execute(f'SELECT id, price, price_currency from part_partsellpricebreak;') - - results = cursor.fetchall() - - codes_in_use = set() - - for index, row in enumerate(results): - pk, price, code = row - - codes_in_use.add(code) - - # Copy the 'price' field back into the 'cost' field - response = cursor.execute(f'UPDATE part_partsellpricebreak set cost={price} where id={pk};') - - # Keep a dict of which currency objects map to which code - code_map = {} - - # For each currency code in use, check if we have a matching Currency object - for code in codes_in_use: - response = cursor.execute(f"SELECT id, suffix from common_currency where suffix='{code}';") - row = cursor.fetchone() - - if row is not None: - # A match exists! - pk, suffix = row - code_map[suffix] = pk - else: - # No currency object exists! - description = CURRENCIES[code] - - # Create a new object in the database - print(f"Creating new Currency object for {code}") - - # Construct a query to create a new Currency object - query = f"INSERT into common_currency (symbol, suffix, description, value, base) VALUES ('$', '{code}', '{description}', 1.0, False);" - - response = cursor.execute(query) - - code_map[code] = cursor.lastrowid - - # Ok, now we know how each suffix maps to a Currency object - for suffix in code_map.keys(): - pk = code_map[suffix] - - # Update the table to point to the Currency objects - print(f"Currency {suffix} -> pk {pk}") - - response = cursor.execute(f"UPDATE part_partsellpricebreak set currency_id={pk} where price_currency='{suffix}';") - - -class Migration(migrations.Migration): - - atomic = False - - dependencies = [ - ('part', '0055_auto_20201110_1001'), - ] - - operations = [ - migrations.RunPython(migrate_currencies, reverse_code=reverse_currencies), - ] diff --git a/src/backend/InvenTree/part/migrations/0057_remove_partsellpricebreak_currency.py b/src/backend/InvenTree/part/migrations/0057_remove_partsellpricebreak_currency.py deleted file mode 100644 index 974aecef4b..0000000000 --- a/src/backend/InvenTree/part/migrations/0057_remove_partsellpricebreak_currency.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-10 11:40 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0056_auto_20201110_1125'), - ] - - operations = [ - migrations.RemoveField( - model_name='partsellpricebreak', - name='currency', - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0058_remove_partsellpricebreak_cost.py b/src/backend/InvenTree/part/migrations/0058_remove_partsellpricebreak_cost.py deleted file mode 100644 index dcf625aa6f..0000000000 --- a/src/backend/InvenTree/part/migrations/0058_remove_partsellpricebreak_cost.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-10 11:42 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0057_remove_partsellpricebreak_currency'), - ] - - operations = [ - migrations.RemoveField( - model_name='partsellpricebreak', - name='cost', - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0059_auto_20201112_1112.py b/src/backend/InvenTree/part/migrations/0059_auto_20201112_1112.py deleted file mode 100644 index 5075b8431c..0000000000 --- a/src/backend/InvenTree/part/migrations/0059_auto_20201112_1112.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-12 00:12 - -import InvenTree.fields -import django.core.validators -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0058_remove_partsellpricebreak_cost'), - ] - - operations = [ - migrations.AlterField( - model_name='partsellpricebreak', - name='quantity', - field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Price break quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(1)], verbose_name='Quantity'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0060_merge_20201112_1722.py b/src/backend/InvenTree/part/migrations/0060_merge_20201112_1722.py deleted file mode 100644 index c53e2ddd68..0000000000 --- a/src/backend/InvenTree/part/migrations/0060_merge_20201112_1722.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 3.0.7 on 2020-11-12 06:22 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0053_partcategoryparametertemplate'), - ('part', '0059_auto_20201112_1112'), - ] - - operations = [ - ] diff --git a/src/backend/InvenTree/part/migrations/0061_auto_20210103_2313.py b/src/backend/InvenTree/part/migrations/0061_auto_20210103_2313.py deleted file mode 100644 index baf52cc811..0000000000 --- a/src/backend/InvenTree/part/migrations/0061_auto_20210103_2313.py +++ /dev/null @@ -1,83 +0,0 @@ -# Generated by Django 3.0.7 on 2021-01-03 12:13 - -import InvenTree.fields -from django.db import migrations, models -import django.db.models.deletion -import mptt.fields -import part.settings - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0055_auto_20201117_1453'), - ('part', '0060_merge_20201112_1722'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='IPN', - field=models.CharField(blank=True, help_text='Internal Part Number', max_length=100, null=True, verbose_name='IPN'), - ), - migrations.AlterField( - model_name='part', - name='assembly', - field=models.BooleanField(default=part.settings.part_assembly_default, help_text='Can this part be built from other parts?', verbose_name='Assembly'), - ), - migrations.AlterField( - model_name='part', - name='category', - field=mptt.fields.TreeForeignKey(blank=True, help_text='Part category', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='parts', to='part.PartCategory', verbose_name='Category'), - ), - migrations.AlterField( - model_name='part', - name='default_location', - field=mptt.fields.TreeForeignKey(blank=True, help_text='Where is this item normally stored?', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_parts', to='stock.StockLocation', verbose_name='Default Location'), - ), - migrations.AlterField( - model_name='part', - name='description', - field=models.CharField(help_text='Part description', max_length=250, verbose_name='Description'), - ), - migrations.AlterField( - model_name='part', - name='is_template', - field=models.BooleanField(default=part.settings.part_template_default, help_text='Is this part a template part?', verbose_name='Is Template'), - ), - migrations.AlterField( - model_name='part', - name='keywords', - field=models.CharField(blank=True, help_text='Part keywords to improve visibility in search results', max_length=250, null=True, verbose_name='Keywords'), - ), - migrations.AlterField( - model_name='part', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', null=True, verbose_name='Link'), - ), - migrations.AlterField( - model_name='part', - name='name', - field=models.CharField(help_text='Part name', max_length=100, verbose_name='Name'), - ), - migrations.AlterField( - model_name='part', - name='notes', - field=models.TextField(blank=True, help_text='Part notes - supports Markdown formatting', null=True, verbose_name='Notes'), - ), - migrations.AlterField( - model_name='part', - name='revision', - field=models.CharField(blank=True, help_text='Part revision or version number', max_length=100, null=True, verbose_name='Revision'), - ), - migrations.AlterField( - model_name='part', - name='variant_of', - field=models.ForeignKey(blank=True, help_text='Is this part a variant of another part?', limit_choices_to={'active': True, 'is_template': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='variants', to='part.Part', verbose_name='Variant Of'), - ), - migrations.AlterField( - model_name='part', - name='virtual', - field=models.BooleanField(default=part.settings.part_virtual_default, help_text='Is this a virtual part, such as a software product or license?', verbose_name='Virtual'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0061_auto_20210103_2313_squashed_0142_remove_part_last_stocktake_remove_partstocktake_note_and_more.py b/src/backend/InvenTree/part/migrations/0061_auto_20210103_2313_squashed_0142_remove_part_last_stocktake_remove_partstocktake_note_and_more.py new file mode 100644 index 0000000000..3d2e298442 --- /dev/null +++ b/src/backend/InvenTree/part/migrations/0061_auto_20210103_2313_squashed_0142_remove_part_last_stocktake_remove_partstocktake_note_and_more.py @@ -0,0 +1,1006 @@ +# Generated by Django 5.2.17 on 2026-09-09 01:10 + +import InvenTree.fields +import InvenTree.validators +import common.currency +import common.icons +import django.core.validators +import django.db.models.deletion +import djmoney.models.fields +import djmoney.models.validators +import mptt.fields +import part.models +import part.settings +import stdimage.models +import taggit.managers +from django.conf import settings +from django.db import migrations, models + + +def fake_func(*args, **kwargs): + """A placeholder function to avoid import errors.""" + + +class RemoveFieldOrSkip(migrations.RemoveField): + """Custom RemoveField operation which will fail gracefully if the field does not exist. + + Ref: https://stackoverflow.com/questions/58518726/how-to-ignore-a-specific-migration + """ + + def database_backwards(self, app_label, schema_editor, from_state, to_state) -> None: + """Backwards migration should not do anything.""" + + def database_forwards(self, app_label, schema_editor, from_state, to_state) -> None: + """Forwards migration *attempts* to remove existing fields, but will fail gracefully if they do not exist.""" + try: + super().database_forwards(app_label, schema_editor, from_state, to_state) + except Exception: + pass + + def state_forwards(self, app_label, state) -> None: + try: + super().state_forwards(app_label, state) + except Exception: + pass + + +class AddFieldOrSkip(migrations.AddField): + """Custom AddField operation which will fail gracefully if the field already exists. + + Ref: https://stackoverflow.com/questions/58518726/how-to-ignore-a-specific-migration + """ + + def database_backwards(self, app_label, schema_editor, from_state, to_state) -> None: + """Backwards migration should not do anything.""" + + def database_forwards(self, app_label, schema_editor, from_state, to_state) -> None: + """Forwards migration *attempts* to remove existing fields, but will fail gracefully if they do not exist.""" + try: + super().database_forwards(app_label, schema_editor, from_state, to_state) + except Exception: + pass + + def state_forwards(self, app_label, state) -> None: + try: + super().state_forwards(app_label, state) + except Exception: + pass + + +class Migration(migrations.Migration): + + replaces = [('part', '0061_auto_20210103_2313'), ('part', '0062_merge_20210105_0056'), ('part', '0063_bomitem_inherited'), ('part', '0064_auto_20210404_2016'), ('part', '0065_auto_20210505_2144'), ('part', '0066_bomitem_allow_variants'), ('part', '0067_partinternalpricebreak'), ('part', '0068_part_unique_part'), ('part', '0069_auto_20210701_0509'), ('part', '0070_alter_part_variant_of'), ('part', '0071_alter_partparametertemplate_name'), ('part', '0072_bomitemsubstitute'), ('part', '0073_auto_20211013_1048'), ('part', '0074_partcategorystar'), ('part', '0075_auto_20211128_0151'), ('part', '0076_auto_20220516_0819'), ('part', '0077_alter_bomitem_unique_together'), ('part', '0078_auto_20220606_0024'), ('part', '0079_alter_part_notes'), ('part', '0080_alter_part_image'), ('part', '0081_alter_partcategory_name'), ('part', '0082_partcategory_pathstring'), ('part', '0083_auto_20220731_2357'), ('part', '0084_partcategory_icon'), ('part', '0085_partparametertemplate_description'), ('part', '0086_auto_20220912_0007'), ('part', '0087_bomitem_consumable'), ('part', '0088_alter_partparametertemplate_name'), ('part', '0089_auto_20221112_0128'), ('part', '0090_auto_20221115_0816'), ('part', '0091_partstocktake'), ('part', '0092_part_last_stocktake'), ('part', '0093_auto_20230115_1404'), ('part', '0094_alter_part_units'), ('part', '0095_alter_part_responsible'), ('part', '0096_auto_20230211_0029'), ('part', '0097_partstocktakereport'), ('part', '0098_auto_20230214_1115'), ('part', '0099_alter_bomitem_inherited'), ('part', '0100_alter_bomitem_reference'), ('part', '0101_bomitem_validated'), ('part', '0102_auto_20230314_0112'), ('part', '0103_auto_20230317_0816'), ('part', '0104_alter_part_description'), ('part', '0105_alter_part_notes'), ('part', '0106_part_tags'), ('part', '0107_alter_part_tags'), ('part', '0108_auto_20230516_1334'), ('part', '0109_auto_20230517_1048'), ('part', '0110_alter_part_units'), ('part', '0111_auto_20230521_1350'), ('part', '0112_auto_20230525_1606'), ('part', '0113_auto_20230531_1205'), ('part', '0114_alter_part_minimum_stock'), ('part', '0115_part_responsible_owner'), ('part', '0116_auto_20231023_0332'), ('part', '0117_remove_part_responsible'), ('part', '0118_auto_20231024_1844'), ('part', '0119_auto_20231120_0457'), ('part', '0120_parttesttemplate_key'), ('part', '0121_auto_20240207_0344'), ('part', '0122_parttesttemplate_enabled'), ('part', '0123_parttesttemplate_choices'), ('part', '0124_delete_partattachment'), ('part', '0125_part_locked'), ('part', '0126_part_revision_of'), ('part', '0127_remove_partcategory_icon_partcategory__icon'), ('part', '0128_part_testable'), ('part', '0129_auto_20240815_0214'), ('part', '0130_alter_parttesttemplate_part'), ('part', '0131_partrelated_note'), ('part', '0132_partparametertemplate_selectionlist'), ('part', '0133_alter_partcategory__icon'), ('part', '0134_auto_20250221_1309'), ('part', '0135_alter_part_link'), ('part', '0136_partparameter_note_partparameter_updated_and_more'), ('part', '0137_bomitem_attrition_bomitem_rounding_multiple_and_more'), ('part', '0138_auto_20250718_2342'), ('part', '0139_remove_bomitem_overage'), ('part', '0140_part_bom_validated'), ('part', '0141_auto_20250722_0303'), ('part', '0142_remove_part_last_stocktake_remove_partstocktake_note_and_more')] + + dependencies = [ + ('common', '0032_selectionlist_selectionlistentry_and_more'), + ('part', '0060_merge_20201112_1722'), + ('stock', '0055_auto_20201117_1453'), + ('stock', '0058_stockitem_packaging'), + ('taggit', '0005_auto_20220424_2025'), + ('users', '0005_owner_model'), + ('users', '0009_auto_20231020_2356'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name='part', + name='IPN', + field=models.CharField(blank=True, help_text='Internal Part Number', max_length=100, null=True, verbose_name='IPN'), + ), + migrations.AlterField( + model_name='part', + name='assembly', + field=models.BooleanField(default=part.settings.part_assembly_default, help_text='Can this part be built from other parts?', verbose_name='Assembly'), + ), + migrations.AlterField( + model_name='part', + name='category', + field=mptt.fields.TreeForeignKey(blank=True, help_text='Part category', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='parts', to='part.partcategory', verbose_name='Category'), + ), + migrations.AlterField( + model_name='part', + name='default_location', + field=mptt.fields.TreeForeignKey(blank=True, help_text='Where is this item normally stored?', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_parts', to='stock.stocklocation', verbose_name='Default Location'), + ), + migrations.AddField( + model_name='part', + name='default_expiry', + field=models.PositiveIntegerField(default=0, help_text='Expiry time (in days) for stock items of this part', validators=[django.core.validators.MinValueValidator(0)], verbose_name='Default Expiry'), + ), + migrations.AlterField( + model_name='part', + name='description', + field=models.CharField(help_text='Part description', max_length=250, verbose_name='Description'), + ), + migrations.AlterField( + model_name='part', + name='is_template', + field=models.BooleanField(default=part.settings.part_template_default, help_text='Is this part a template part?', verbose_name='Is Template'), + ), + migrations.AlterField( + model_name='part', + name='keywords', + field=models.CharField(blank=True, help_text='Part keywords to improve visibility in search results', max_length=250, null=True, verbose_name='Keywords'), + ), + migrations.AlterField( + model_name='part', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=2000, null=True, verbose_name='Link'), + ), + migrations.AlterField( + model_name='part', + name='name', + field=models.CharField(help_text='Part name', max_length=100, verbose_name='Name'), + ), + migrations.AlterField( + model_name='part', + name='notes', + field=models.TextField(blank=True, help_text='Part notes - supports Markdown formatting', null=True, verbose_name='Notes'), + ), + migrations.AlterField( + model_name='part', + name='revision', + field=models.CharField(blank=True, help_text='Part revision or version number', max_length=100, null=True, verbose_name='Revision'), + ), + migrations.AlterField( + model_name='part', + name='variant_of', + field=models.ForeignKey(blank=True, help_text='Is this part a variant of another part?', limit_choices_to={'active': True, 'is_template': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='variants', to='part.part', verbose_name='Variant Of'), + ), + migrations.AlterField( + model_name='part', + name='virtual', + field=models.BooleanField(default=part.settings.part_virtual_default, help_text='Is this a virtual part, such as a software product or license?', verbose_name='Virtual'), + ), + migrations.AddField( + model_name='bomitem', + name='inherited', + field=models.BooleanField(default=False, help_text='This BOM item is inherited by BOMs for variant parts', verbose_name='Inherited'), + ), + migrations.AlterField( + model_name='bomitem', + name='checksum', + field=models.CharField(blank=True, help_text='BOM line checksum', max_length=128, verbose_name='Checksum'), + ), + migrations.AlterField( + model_name='bomitem', + name='note', + field=models.CharField(blank=True, help_text='BOM item notes', max_length=500, verbose_name='Note'), + ), + migrations.AlterField( + model_name='bomitem', + name='optional', + field=models.BooleanField(default=False, help_text='This BOM item is optional', verbose_name='Optional'), + ), + migrations.AlterField( + model_name='bomitem', + name='overage', + field=models.CharField(blank=True, help_text='Estimated build wastage quantity (absolute or percentage)', max_length=24, verbose_name='Overage'), + ), + migrations.AlterField( + model_name='bomitem', + name='part', + field=models.ForeignKey(help_text='Select parent part', limit_choices_to={'assembly': True}, on_delete=django.db.models.deletion.CASCADE, related_name='bom_items', to='part.part', verbose_name='Part'), + ), + migrations.AlterField( + model_name='bomitem', + name='quantity', + field=models.DecimalField(decimal_places=5, default=1.0, help_text='BOM quantity for this BOM item', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'), + ), + migrations.AlterField( + model_name='bomitem', + name='reference', + field=models.CharField(blank=True, help_text='BOM item reference', max_length=500, verbose_name='Reference'), + ), + migrations.AlterField( + model_name='bomitem', + name='sub_part', + field=models.ForeignKey(help_text='Select part to be used in BOM', limit_choices_to={'component': True}, on_delete=django.db.models.deletion.CASCADE, related_name='used_in', to='part.part', verbose_name='Sub part'), + ), + migrations.AlterField( + model_name='part', + name='bom_checked_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='boms_checked', to=settings.AUTH_USER_MODEL, verbose_name='BOM checked by'), + ), + migrations.AlterField( + model_name='part', + name='bom_checked_date', + field=models.DateField(blank=True, null=True, verbose_name='BOM checked date'), + ), + migrations.AlterField( + model_name='part', + name='bom_checksum', + field=models.CharField(blank=True, help_text='Stored BOM checksum', max_length=128, verbose_name='BOM checksum'), + ), + migrations.AlterField( + model_name='part', + name='creation_date', + field=models.DateField(auto_now_add=True, null=True, verbose_name='Creation Date'), + ), + migrations.AlterField( + model_name='part', + name='creation_user', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parts_created', to=settings.AUTH_USER_MODEL, verbose_name='Creation User'), + ), + migrations.AlterField( + model_name='part', + name='image', + field=stdimage.models.StdImageField(blank=True, force_min_size=False, null=True, upload_to=part.models.rename_part_image, variations={}, verbose_name='Image'), + ), + migrations.AlterField( + model_name='part', + name='responsible', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parts_responible', to=settings.AUTH_USER_MODEL, verbose_name='Responsible'), + ), + migrations.AlterField( + model_name='partattachment', + name='attachment', + field=models.FileField(help_text='Select file to attach', upload_to='attachments', verbose_name='Attachment'), + ), + migrations.AlterField( + model_name='partattachment', + name='comment', + field=models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment'), + ), + migrations.AlterField( + model_name='partattachment', + name='part', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='part.part', verbose_name='Part'), + ), + migrations.AlterField( + model_name='partattachment', + name='upload_date', + field=models.DateField(auto_now_add=True, null=True, verbose_name='upload date'), + ), + migrations.AlterField( + model_name='partattachment', + name='user', + field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User'), + ), + migrations.AlterField( + model_name='partcategory', + name='default_keywords', + field=models.CharField(blank=True, help_text='Default keywords for parts in this category', max_length=250, null=True, verbose_name='Default keywords'), + ), + migrations.AlterField( + model_name='partcategory', + name='default_location', + field=mptt.fields.TreeForeignKey(blank=True, help_text='Default location for parts in this category', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_categories', to='stock.stocklocation', verbose_name='Default Location'), + ), + migrations.AlterField( + model_name='partcategory', + name='description', + field=models.CharField(blank=True, help_text='Description (optional)', max_length=250, verbose_name='Description'), + ), + migrations.AlterField( + model_name='partcategory', + name='name', + field=models.CharField(help_text='Name', max_length=100, validators=[InvenTree.validators.validate_tree_name], verbose_name='Name'), + ), + migrations.AlterField( + model_name='partcategory', + name='parent', + field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='part.partcategory', verbose_name='parent'), + ), + migrations.AlterField( + model_name='partcategoryparametertemplate', + name='category', + field=models.ForeignKey(help_text='Part Category', on_delete=django.db.models.deletion.CASCADE, related_name='parameter_templates', to='part.partcategory', verbose_name='Category'), + ), + migrations.AlterField( + model_name='partcategoryparametertemplate', + name='default_value', + field=models.CharField(blank=True, help_text='Default Parameter Value', max_length=500, verbose_name='Default Value'), + ), + migrations.AlterField( + model_name='partcategoryparametertemplate', + name='parameter_template', + field=models.ForeignKey(help_text='Parameter Template', on_delete=django.db.models.deletion.CASCADE, related_name='part_categories', to='part.partparametertemplate', verbose_name='Parameter Template'), + ), + migrations.AlterField( + model_name='partparameter', + name='data', + field=models.CharField(help_text='Parameter Value', max_length=500, verbose_name='Data'), + ), + migrations.AlterField( + model_name='partparameter', + name='part', + field=models.ForeignKey(help_text='Parent Part', on_delete=django.db.models.deletion.CASCADE, related_name='parameters', to='part.part', verbose_name='Part'), + ), + migrations.AlterField( + model_name='partparameter', + name='template', + field=models.ForeignKey(help_text='Parameter Template', on_delete=django.db.models.deletion.CASCADE, related_name='instances', to='part.partparametertemplate', verbose_name='Template'), + ), + migrations.AlterField( + model_name='partparametertemplate', + name='name', + field=models.CharField(help_text='Parameter Name', max_length=100, unique=True, verbose_name='Name'), + ), + migrations.AlterField( + model_name='partparametertemplate', + name='units', + field=models.CharField(blank=True, help_text='Parameter Units', max_length=25, verbose_name='Units'), + ), + migrations.AlterField( + model_name='partrelated', + name='part_1', + field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='related_parts_1', to='part.part', verbose_name='Part 1'), + ), + migrations.AlterField( + model_name='partrelated', + name='part_2', + field=models.ForeignKey(help_text='Select Related Part', on_delete=django.db.models.deletion.DO_NOTHING, related_name='related_parts_2', to='part.part', verbose_name='Part 2'), + ), + migrations.AlterField( + model_name='partsellpricebreak', + name='part', + field=models.ForeignKey(limit_choices_to={'salable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='salepricebreaks', to='part.part', verbose_name='Part'), + ), + migrations.AlterField( + model_name='partstar', + name='part', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='starred_users', to='part.part', verbose_name='Part'), + ), + migrations.AlterField( + model_name='partstar', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='starred_parts', to=settings.AUTH_USER_MODEL, verbose_name='User'), + ), + migrations.AlterField( + model_name='parttesttemplate', + name='part', + field=models.ForeignKey(limit_choices_to={'trackable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='test_templates', to='part.part', verbose_name='Part'), + ), + migrations.AddField( + model_name='part', + name='base_cost', + field=models.DecimalField(decimal_places=3, default=0, help_text='Minimum charge (e.g. stocking fee)', max_digits=10, validators=[django.core.validators.MinValueValidator(0)], verbose_name='base cost'), + ), + migrations.AddField( + model_name='part', + name='multiple', + field=models.PositiveIntegerField(default=1, help_text='Sell multiple', validators=[django.core.validators.MinValueValidator(1)], verbose_name='multiple'), + ), + migrations.AddField( + model_name='bomitem', + name='allow_variants', + field=models.BooleanField(default=False, help_text='Stock items for variant parts can be used for this BOM item', verbose_name='Allow Variants'), + ), + migrations.CreateModel( + name='PartInternalPriceBreak', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity', InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Price break quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(1)], verbose_name='Quantity')), + ('price_currency', djmoney.models.fields.CurrencyField(choices=[('AUD', 'AUD - Australian Dollar'), ('CAD', 'CAD - Canadian Dollar'), ('CNY', 'CNY - Chinese Yuan'), ('EUR', 'EUR - Euro'), ('GBP', 'GBP - British Pound'), ('JPY', 'JPY - Japanese Yen'), ('NZD', 'NZD - New Zealand Dollar'), ('USD', 'USD - US Dollar')], default='', editable=False, max_length=3)), + ('price', djmoney.models.fields.MoneyField(decimal_places=4, default_currency='', help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price')), + ('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='internalpricebreaks', to='part.part', verbose_name='Part')), + ], + options={ + 'unique_together': {('part', 'quantity')}, + 'verbose_name': 'Part Internal Price Break', + }, + ), + migrations.AddConstraint( + model_name='part', + constraint=models.UniqueConstraint(fields=('name', 'IPN', 'revision'), name='unique_part'), + ), + migrations.AlterField( + model_name='partinternalpricebreak', + name='price', + field=InvenTree.fields.InvenTreeModelMoneyField(currency_choices=[], decimal_places=4, default_currency=common.currency.currency_code_default, help_text='Unit price at specified quantity', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price'), + ), + migrations.AlterField( + model_name='partinternalpricebreak', + name='price_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3), + ), + migrations.AlterField( + model_name='partsellpricebreak', + name='price', + field=InvenTree.fields.InvenTreeModelMoneyField(currency_choices=[], decimal_places=4, default_currency=common.currency.currency_code_default, help_text='Unit price at specified quantity', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price'), + ), + migrations.AlterField( + model_name='partsellpricebreak', + name='price_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3), + ), + migrations.AlterField( + model_name='part', + name='variant_of', + field=models.ForeignKey(blank=True, help_text='Is this part a variant of another part?', limit_choices_to={'is_template': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='variants', to='part.part', verbose_name='Variant Of'), + ), + migrations.AlterField( + model_name='partparametertemplate', + name='name', + field=models.CharField(help_text='Parameter Name', max_length=100, unique=True, verbose_name='Name'), + ), + migrations.CreateModel( + name='BomItemSubstitute', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('bom_item', models.ForeignKey(help_text='Parent BOM item', on_delete=django.db.models.deletion.CASCADE, related_name='substitutes', to='part.bomitem', verbose_name='BOM Item')), + ('part', models.ForeignKey(help_text='Substitute part', limit_choices_to={'component': True}, on_delete=django.db.models.deletion.CASCADE, related_name='substitute_items', to='part.part', verbose_name='Part')), + ], + options={ + 'verbose_name': 'BOM Item Substitute', + 'unique_together': {('part', 'bom_item')}, + }, + ), + migrations.CreateModel( + name='PartCategoryStar', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='starred_users', to='part.partcategory', verbose_name='Category')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='starred_categories', to=settings.AUTH_USER_MODEL, verbose_name='User')), + ], + options={ + 'unique_together': {('category', 'user')}, + }, + ), + migrations.AddField( + model_name='partattachment', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=2000, null=True, verbose_name='Link'), + ), + migrations.AlterField( + model_name='partattachment', + name='attachment', + field=models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to='attachments', verbose_name='Attachment'), + ), + migrations.AddField( + model_name='part', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AddField( + model_name='partcategory', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AlterUniqueTogether( + name='bomitem', + unique_together=set(), + ), + migrations.AlterField( + model_name='partrelated', + name='part_1', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='related_parts_1', to='part.part', verbose_name='Part 1'), + ), + migrations.AlterField( + model_name='partrelated', + name='part_2', + field=models.ForeignKey(help_text='Select Related Part', on_delete=django.db.models.deletion.CASCADE, related_name='related_parts_2', to='part.part', verbose_name='Part 2'), + ), + migrations.AlterUniqueTogether( + name='partrelated', + unique_together={('part_1', 'part_2')}, + ), + migrations.AlterField( + model_name='part', + name='notes', + field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Part notes', max_length=50000, null=True, verbose_name='Notes'), + ), + migrations.AlterField( + model_name='part', + name='image', + field=stdimage.models.StdImageField(blank=True, force_min_size=False, null=True, upload_to=part.models.rename_part_image, variations={'preview': (256, 256), 'thumbnail': (128, 128)}, verbose_name='Image'), + ), + migrations.AlterField( + model_name='partcategory', + name='name', + field=models.CharField(help_text='Name', max_length=100, verbose_name='Name'), + ), + migrations.AddField( + model_name='partcategory', + name='pathstring', + field=models.CharField(blank=True, help_text='Path', max_length=250, verbose_name='Path'), + ), + migrations.AddField( + model_name='partcategory', + name='icon', + field=models.CharField(blank=True, help_text='Icon (optional)', max_length=100, verbose_name='Icon'), + ), + migrations.AddField( + model_name='partparametertemplate', + name='description', + field=models.CharField(blank=True, help_text='Parameter description', max_length=250, verbose_name='Description'), + ), + migrations.AddField( + model_name='part', + name='barcode_data', + field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), + ), + migrations.AddField( + model_name='part', + name='barcode_hash', + field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), + ), + migrations.AddField( + model_name='bomitem', + name='consumable', + field=models.BooleanField(default=False, help_text='This BOM item is consumable (it is not tracked in build orders)', verbose_name='Consumable'), + ), + migrations.AlterField( + model_name='partparametertemplate', + name='name', + field=models.CharField(help_text='Parameter Name', max_length=100, unique=True, verbose_name='Name'), + ), + migrations.AlterField( + model_name='part', + name='base_cost', + field=models.DecimalField(decimal_places=6, default=0, help_text='Minimum charge (e.g. stocking fee)', max_digits=19, validators=[django.core.validators.MinValueValidator(0)], verbose_name='base cost'), + ), + migrations.AlterField( + model_name='partinternalpricebreak', + name='price', + field=InvenTree.fields.InvenTreeModelMoneyField(currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Unit price at specified quantity', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price'), + ), + migrations.AlterField( + model_name='partsellpricebreak', + name='price', + field=InvenTree.fields.InvenTreeModelMoneyField(currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Unit price at specified quantity', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price'), + ), + migrations.AddField( + model_name='partcategory', + name='structural', + field=models.BooleanField(default=False, help_text='Parts may not be directly assigned to a structural category, but may be assigned to child categories.', verbose_name='Structural'), + ), + migrations.AddField( + model_name='part', + name='last_stocktake', + field=models.DateField(blank=True, null=True, verbose_name='Last Stocktake'), + ), + migrations.AddField( + model_name='partinternalpricebreak', + name='updated', + field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'), + ), + migrations.AddField( + model_name='partsellpricebreak', + name='updated', + field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'), + ), + migrations.CreateModel( + name='PartPricing', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('currency', models.CharField(choices=[('AUD', 'AUD - Australian Dollar'), ('CAD', 'CAD - Canadian Dollar'), ('CNY', 'CNY - Chinese Yuan'), ('EUR', 'EUR - Euro'), ('GBP', 'GBP - British Pound'), ('JPY', 'JPY - Japanese Yen'), ('NZD', 'NZD - New Zealand Dollar'), ('USD', 'USD - US Dollar')], default=common.currency.currency_code_default, help_text='Currency used to cache pricing calculations', max_length=10, verbose_name='Currency')), + ('updated', models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated')), + ('scheduled_for_update', models.BooleanField(default=False)), + ('bom_cost_min_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('bom_cost_min', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Minimum cost of component parts', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum BOM Cost')), + ('bom_cost_max_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('bom_cost_max', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Maximum cost of component parts', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum BOM Cost')), + ('purchase_cost_min_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('purchase_cost_min', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Minimum historical purchase cost', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum Purchase Cost')), + ('purchase_cost_max_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('purchase_cost_max', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Maximum historical purchase cost', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum Purchase Cost')), + ('internal_cost_min_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('internal_cost_min', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Minimum cost based on internal price breaks', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum Internal Price')), + ('internal_cost_max_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('internal_cost_max', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Maximum cost based on internal price breaks', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum Internal Price')), + ('supplier_price_min_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('supplier_price_min', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Minimum price of part from external suppliers', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum Supplier Price')), + ('supplier_price_max_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('supplier_price_max', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Maximum price of part from external suppliers', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum Supplier Price')), + ('variant_cost_min_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('variant_cost_min', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Calculated minimum cost of variant parts', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum Variant Cost')), + ('variant_cost_max_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('variant_cost_max', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Calculated maximum cost of variant parts', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum Variant Cost')), + ('overall_min_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('overall_min', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Calculated overall minimum cost', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum Cost')), + ('overall_max_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('overall_max', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Calculated overall maximum cost', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum Cost')), + ('sale_price_min_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('sale_price_min', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Minimum sale price based on price breaks', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum Sale Price')), + ('sale_price_max_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('sale_price_max', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Maximum sale price based on price breaks', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum Sale Price')), + ('sale_history_min_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('sale_history_min', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Minimum historical sale price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum Sale Cost')), + ('sale_history_max_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('sale_history_max', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Maximum historical sale price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum Sale Cost')), + ('part', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='pricing_data', to='part.part', verbose_name='Part')), + ], + ), + migrations.AlterField( + model_name='part', + name='units', + field=models.CharField(blank=True, default='', help_text='Units of measure for this part', max_length=20, null=True, verbose_name='Units'), + ), + migrations.AlterField( + model_name='part', + name='responsible', + field=models.ForeignKey(blank=True, help_text='User responsible for this part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parts_responible', to=settings.AUTH_USER_MODEL, verbose_name='Responsible'), + ), + migrations.CreateModel( + name='PartStocktake', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity', models.DecimalField(decimal_places=5, help_text='Total available stock at time of stocktake', max_digits=19, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity')), + ('date', models.DateField(auto_now_add=True, help_text='Date stocktake was performed', verbose_name='Date')), + ('note', models.CharField(blank=True, help_text='Additional notes', max_length=250, verbose_name='Notes')), + ('part', models.ForeignKey(help_text='Part for stocktake', on_delete=django.db.models.deletion.CASCADE, related_name='stocktakes', to='part.part', verbose_name='Part')), + ('user', models.ForeignKey(blank=True, help_text='User who performed this stocktake', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='part_stocktakes', to=settings.AUTH_USER_MODEL, verbose_name='User')), + ('cost_max', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Estimated maximum cost of stock on hand', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum Stock Cost')), + ('cost_max_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('cost_min', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Estimated minimum cost of stock on hand', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum Stock Cost')), + ('cost_min_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), + ('item_count', models.IntegerField(default=1, help_text='Number of individual stock entries at time of stocktake', verbose_name='Item Count')), + ], + ), + migrations.CreateModel( + name='PartStocktakeReport', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateField(auto_now_add=True, verbose_name='Date')), + ('report', models.FileField(help_text='Stocktake report file (generated internally)', upload_to=fake_func, verbose_name='Report')), + ('user', models.ForeignKey(blank=True, help_text='User who requested this stocktake report', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stocktake_reports', to=settings.AUTH_USER_MODEL, verbose_name='User')), + ('part_count', models.IntegerField(default=0, help_text='Number of parts covered by stocktake', verbose_name='Part Count')), + ], + ), + migrations.AlterField( + model_name='bomitem', + name='inherited', + field=models.BooleanField(default=False, help_text='This BOM item is inherited by BOMs for variant parts', verbose_name='Gets inherited'), + ), + migrations.AlterField( + model_name='bomitem', + name='reference', + field=models.CharField(blank=True, help_text='BOM item reference', max_length=5000, verbose_name='Reference'), + ), + migrations.AddField( + model_name='bomitem', + name='validated', + field=models.BooleanField(default=False, help_text='This BOM item has been validated', verbose_name='Validated'), + ), + migrations.AddField( + model_name='bomitem', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AddField( + model_name='partparametertemplate', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AlterField( + model_name='part', + name='description', + field=models.CharField(blank=True, help_text='Part description (optional)', max_length=250, verbose_name='Description'), + ), + migrations.AlterField( + model_name='part', + name='notes', + field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), + ), + migrations.AddField( + model_name='part', + name='tags', + field=taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), + ), + migrations.AddField( + model_name='partparameter', + name='data_numeric', + field=models.FloatField(blank=True, default=None, null=True), + ), + migrations.AlterField( + model_name='partparameter', + name='data', + field=models.CharField(help_text='Parameter Value', max_length=500, validators=[django.core.validators.MinLengthValidator(1)], verbose_name='Data'), + ), + migrations.AlterField( + model_name='partparametertemplate', + name='units', + field=models.CharField(blank=True, help_text='Physical units for this parameter', max_length=25, validators=[InvenTree.validators.validate_physical_units], verbose_name='Units'), + ), + migrations.AlterField( + model_name='part', + name='units', + field=models.CharField(blank=True, default='', help_text='Units of measure for this part', max_length=20, null=True, validators=[InvenTree.validators.validate_physical_units], verbose_name='Units'), + ), + RemoveFieldOrSkip( + model_name='partparametertemplate', + name='checkbox', + ), + RemoveFieldOrSkip( + model_name='partparametertemplate', + name='choices', + ), + AddFieldOrSkip( + model_name='partparametertemplate', + name='checkbox', + field=models.BooleanField(default=False, help_text='Is this parameter a checkbox?', verbose_name='Checkbox'), + ), + AddFieldOrSkip( + model_name='partparametertemplate', + name='choices', + field=models.CharField(blank=True, help_text='Valid choices for this parameter (comma-separated)', max_length=5000, verbose_name='Choices'), + ), + migrations.AddField( + model_name='bomitemsubstitute', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AddField( + model_name='partcategoryparametertemplate', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AddField( + model_name='partparameter', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AddField( + model_name='partrelated', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AddField( + model_name='parttesttemplate', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AlterField( + model_name='part', + name='minimum_stock', + field=models.DecimalField(decimal_places=6, default=0, help_text='Minimum allowed stock level', max_digits=19, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Minimum Stock'), + ), + migrations.AddField( + model_name='part', + name='responsible_owner', + field=models.ForeignKey(blank=True, help_text='Owner responsible for this part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parts_responsible', to='users.owner', verbose_name='Responsible'), + ), + migrations.RemoveField( + model_name='part', + name='responsible', + ), + migrations.AlterField( + model_name='partinternalpricebreak', + name='price_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partpricing', + name='bom_cost_max_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partpricing', + name='bom_cost_min_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partpricing', + name='internal_cost_max_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partpricing', + name='internal_cost_min_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partpricing', + name='overall_max_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partpricing', + name='overall_min_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partpricing', + name='purchase_cost_max_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partpricing', + name='purchase_cost_min_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partpricing', + name='sale_history_max_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partpricing', + name='sale_history_min_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partpricing', + name='sale_price_max_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partpricing', + name='sale_price_min_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partpricing', + name='supplier_price_max_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partpricing', + name='supplier_price_min_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partpricing', + name='variant_cost_max_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partpricing', + name='variant_cost_min_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partsellpricebreak', + name='price_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partstocktake', + name='cost_max_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AlterField( + model_name='partstocktake', + name='cost_min_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AddField( + model_name='partpricing', + name='override_max', + field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Override maximum cost', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum Cost'), + ), + migrations.AddField( + model_name='partpricing', + name='override_max_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AddField( + model_name='partpricing', + name='override_min', + field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Override minimum cost', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum Cost'), + ), + migrations.AddField( + model_name='partpricing', + name='override_min_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AddField( + model_name='parttesttemplate', + name='key', + field=models.CharField(blank=True, help_text='Simplified key for the test', max_length=100, verbose_name='Test Key'), + ), + migrations.AddField( + model_name='parttesttemplate', + name='enabled', + field=models.BooleanField(default=True, help_text='Is this test enabled?', verbose_name='Enabled'), + ), + migrations.AddField( + model_name='parttesttemplate', + name='choices', + field=models.CharField(blank=True, help_text='Valid choices for this test (comma-separated)', max_length=5000, verbose_name='Choices'), + ), + migrations.DeleteModel( + name='PartAttachment', + ), + migrations.AddField( + model_name='part', + name='locked', + field=models.BooleanField(default=False, help_text='Locked parts cannot be edited', verbose_name='Locked'), + ), + migrations.AddField( + model_name='part', + name='revision_of', + field=models.ForeignKey(blank=True, help_text='Is this part a revision of another part?', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='revisions', to='part.part', verbose_name='Revision Of'), + ), + migrations.SeparateDatabaseAndState( + state_operations=[ + migrations.RenameField( + model_name='partcategory', + old_name='icon', + new_name='_icon', + ), + migrations.AlterField( + model_name='partcategory', + name='_icon', + field=models.CharField(blank=True, db_column='icon', help_text='Icon (optional)', max_length=100, validators=[common.icons.validate_icon], verbose_name='Icon'), + ), + ], + ), + migrations.AddField( + model_name='part', + name='testable', + field=models.BooleanField(default=False, help_text='Can this part have test results recorded against it?', verbose_name='Testable'), + ), + migrations.AlterField( + model_name='parttesttemplate', + name='part', + field=models.ForeignKey(limit_choices_to={'testable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='test_templates', to='part.part', verbose_name='Part'), + ), + migrations.AddField( + model_name='partrelated', + name='note', + field=models.CharField(blank=True, help_text='Note for this relationship', max_length=500, verbose_name='Note'), + ), + migrations.AddField( + model_name='partparametertemplate', + name='selectionlist', + field=models.ForeignKey(blank=True, help_text='Selection list for this parameter', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parameter_templates', to='common.selectionlist', verbose_name='Selection List'), + ), + migrations.AlterField( + model_name='partcategory', + name='_icon', + field=models.CharField(blank=True, db_column='icon', help_text='Icon (optional)', max_length=100, null=True, validators=[common.icons.validate_icon], verbose_name='Icon'), + ), + migrations.AlterField( + model_name='part', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=2000, null=True, verbose_name='Link'), + ), + migrations.AddField( + model_name='partparameter', + name='note', + field=models.CharField(blank=True, help_text='Optional note field', max_length=500, verbose_name='Note'), + ), + migrations.AddField( + model_name='partparameter', + name='updated', + field=models.DateTimeField(blank=True, default=None, help_text='Timestamp of last update', null=True, verbose_name='Updated'), + ), + migrations.AddField( + model_name='partparameter', + name='updated_by', + field=models.ForeignKey(blank=True, help_text='User who last updated this object', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(class)s_updated', to=settings.AUTH_USER_MODEL, verbose_name='Update By'), + ), + migrations.AddField( + model_name='bomitem', + name='attrition', + field=models.DecimalField(decimal_places=3, default=0, help_text='Estimated attrition for a build, expressed as a percentage (0-100)', max_digits=6, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(100)], verbose_name='Attrition'), + ), + migrations.AddField( + model_name='bomitem', + name='rounding_multiple', + field=models.DecimalField(blank=True, decimal_places=5, default=None, help_text='Round up required production quantity to nearest multiple of this value', max_digits=15, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Rounding Multiple'), + ), + migrations.AddField( + model_name='bomitem', + name='setup_quantity', + field=models.DecimalField(decimal_places=5, default=0, help_text='Extra required quantity for a build, to account for setup losses', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Setup Quantity'), + ), + migrations.RemoveField( + model_name='bomitem', + name='overage', + ), + migrations.AddField( + model_name='part', + name='bom_validated', + field=models.BooleanField(default=False, help_text='Is the BOM for this part valid?', verbose_name='BOM Validated'), + ), + migrations.RemoveField( + model_name='part', + name='last_stocktake', + ), + migrations.RemoveField( + model_name='partstocktake', + name='note', + ), + migrations.RemoveField( + model_name='partstocktake', + name='user', + ), + migrations.DeleteModel( + name='PartStocktakeReport', + ), + ] diff --git a/src/backend/InvenTree/part/migrations/0061_auto_20210104_2331.py b/src/backend/InvenTree/part/migrations/0061_auto_20210104_2331.py deleted file mode 100644 index c40b611b29..0000000000 --- a/src/backend/InvenTree/part/migrations/0061_auto_20210104_2331.py +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Django 3.0.7 on 2021-01-04 12:31 - -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0031_auto_20210103_2215'), - ('part', '0060_merge_20201112_1722'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='default_expiry', - field=models.PositiveIntegerField(default=0, help_text='Expiry time (in days) for stock items of this part', validators=[django.core.validators.MinValueValidator(0)], verbose_name='Default Expiry'), - ), - migrations.AlterField( - model_name='part', - name='default_supplier', - field=models.ForeignKey(blank=True, help_text='Default supplier part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_parts', to='company.SupplierPart', verbose_name='Default Supplier'), - ), - migrations.AlterField( - model_name='part', - name='minimum_stock', - field=models.PositiveIntegerField(default=0, help_text='Minimum allowed stock level', validators=[django.core.validators.MinValueValidator(0)], verbose_name='Minimum Stock'), - ), - migrations.AlterField( - model_name='part', - name='units', - field=models.CharField(blank=True, default='', help_text='Stock keeping units for this part', max_length=20, null=True, verbose_name='Units'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0062_merge_20210105_0056.py b/src/backend/InvenTree/part/migrations/0062_merge_20210105_0056.py deleted file mode 100644 index 4a8f4378f4..0000000000 --- a/src/backend/InvenTree/part/migrations/0062_merge_20210105_0056.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 3.0.7 on 2021-01-04 13:56 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0061_auto_20210104_2331'), - ('part', '0061_auto_20210103_2313'), - ] - - operations = [ - ] diff --git a/src/backend/InvenTree/part/migrations/0063_bomitem_inherited.py b/src/backend/InvenTree/part/migrations/0063_bomitem_inherited.py deleted file mode 100644 index 569236fd72..0000000000 --- a/src/backend/InvenTree/part/migrations/0063_bomitem_inherited.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.7 on 2021-02-17 10:51 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0062_merge_20210105_0056'), - ] - - operations = [ - migrations.AddField( - model_name='bomitem', - name='inherited', - field=models.BooleanField(default=False, help_text='This BOM item is inherited by BOMs for variant parts', verbose_name='Inherited'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0064_auto_20210404_2016.py b/src/backend/InvenTree/part/migrations/0064_auto_20210404_2016.py deleted file mode 100644 index cf15ab0bea..0000000000 --- a/src/backend/InvenTree/part/migrations/0064_auto_20210404_2016.py +++ /dev/null @@ -1,218 +0,0 @@ -# Generated by Django 3.0.7 on 2021-04-04 20:16 - -import InvenTree.models -import InvenTree.validators -from django.conf import settings -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion -import mptt.fields -import part.models -import stdimage.models - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('stock', '0058_stockitem_packaging'), - ('part', '0063_bomitem_inherited'), - ] - - operations = [ - migrations.AlterField( - model_name='bomitem', - name='checksum', - field=models.CharField(blank=True, help_text='BOM line checksum', max_length=128, verbose_name='Checksum'), - ), - migrations.AlterField( - model_name='bomitem', - name='note', - field=models.CharField(blank=True, help_text='BOM item notes', max_length=500, verbose_name='Note'), - ), - migrations.AlterField( - model_name='bomitem', - name='optional', - field=models.BooleanField(default=False, help_text='This BOM item is optional', verbose_name='Optional'), - ), - migrations.AlterField( - model_name='bomitem', - name='overage', - field=models.CharField(blank=True, help_text='Estimated build wastage quantity (absolute or percentage)', max_length=24, validators=[], verbose_name='Overage'), - ), - migrations.AlterField( - model_name='bomitem', - name='part', - field=models.ForeignKey(help_text='Select parent part', limit_choices_to={'assembly': True}, on_delete=django.db.models.deletion.CASCADE, related_name='bom_items', to='part.Part', verbose_name='Part'), - ), - migrations.AlterField( - model_name='bomitem', - name='quantity', - field=models.DecimalField(decimal_places=5, default=1.0, help_text='BOM quantity for this BOM item', max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'), - ), - migrations.AlterField( - model_name='bomitem', - name='reference', - field=models.CharField(blank=True, help_text='BOM item reference', max_length=500, verbose_name='Reference'), - ), - migrations.AlterField( - model_name='bomitem', - name='sub_part', - field=models.ForeignKey(help_text='Select part to be used in BOM', limit_choices_to={'component': True}, on_delete=django.db.models.deletion.CASCADE, related_name='used_in', to='part.Part', verbose_name='Sub part'), - ), - migrations.AlterField( - model_name='part', - name='bom_checked_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='boms_checked', to=settings.AUTH_USER_MODEL, verbose_name='BOM checked by'), - ), - migrations.AlterField( - model_name='part', - name='bom_checked_date', - field=models.DateField(blank=True, null=True, verbose_name='BOM checked date'), - ), - migrations.AlterField( - model_name='part', - name='bom_checksum', - field=models.CharField(blank=True, help_text='Stored BOM checksum', max_length=128, verbose_name='BOM checksum'), - ), - migrations.AlterField( - model_name='part', - name='creation_date', - field=models.DateField(auto_now_add=True, null=True, verbose_name='Creation Date'), - ), - migrations.AlterField( - model_name='part', - name='creation_user', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parts_created', to=settings.AUTH_USER_MODEL, verbose_name='Creation User'), - ), - migrations.AlterField( - model_name='part', - name='image', - field=stdimage.models.StdImageField(blank=True, null=True, upload_to=part.models.rename_part_image, verbose_name='Image'), - ), - migrations.AlterField( - model_name='part', - name='responsible', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parts_responible', to=settings.AUTH_USER_MODEL, verbose_name='Responsible'), - ), - migrations.AlterField( - model_name='partattachment', - name='attachment', - field=models.FileField(help_text='Select file to attach', upload_to='attachments', verbose_name='Attachment'), - ), - migrations.AlterField( - model_name='partattachment', - name='comment', - field=models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment'), - ), - migrations.AlterField( - model_name='partattachment', - name='part', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='part.Part', verbose_name='Part'), - ), - migrations.AlterField( - model_name='partattachment', - name='upload_date', - field=models.DateField(auto_now_add=True, null=True, verbose_name='upload date'), - ), - migrations.AlterField( - model_name='partattachment', - name='user', - field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User'), - ), - migrations.AlterField( - model_name='partcategory', - name='default_keywords', - field=models.CharField(blank=True, help_text='Default keywords for parts in this category', max_length=250, null=True, verbose_name='Default keywords'), - ), - migrations.AlterField( - model_name='partcategory', - name='default_location', - field=mptt.fields.TreeForeignKey(blank=True, help_text='Default location for parts in this category', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_categories', to='stock.StockLocation', verbose_name='Default Location'), - ), - migrations.AlterField( - model_name='partcategory', - name='description', - field=models.CharField(blank=True, help_text='Description (optional)', max_length=250, verbose_name='Description'), - ), - migrations.AlterField( - model_name='partcategory', - name='name', - field=models.CharField(help_text='Name', max_length=100, validators=[InvenTree.validators.validate_tree_name], verbose_name='Name'), - ), - migrations.AlterField( - model_name='partcategory', - name='parent', - field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='part.PartCategory', verbose_name='parent'), - ), - migrations.AlterField( - model_name='partcategoryparametertemplate', - name='category', - field=models.ForeignKey(help_text='Part Category', on_delete=django.db.models.deletion.CASCADE, related_name='parameter_templates', to='part.PartCategory', verbose_name='Category'), - ), - migrations.AlterField( - model_name='partcategoryparametertemplate', - name='default_value', - field=models.CharField(blank=True, help_text='Default Parameter Value', max_length=500, verbose_name='Default Value'), - ), - migrations.AlterField( - model_name='partcategoryparametertemplate', - name='parameter_template', - field=models.ForeignKey(help_text='Parameter Template', on_delete=django.db.models.deletion.CASCADE, related_name='part_categories', to='part.PartParameterTemplate', verbose_name='Parameter Template'), - ), - migrations.AlterField( - model_name='partparameter', - name='data', - field=models.CharField(help_text='Parameter Value', max_length=500, verbose_name='Data'), - ), - migrations.AlterField( - model_name='partparameter', - name='part', - field=models.ForeignKey(help_text='Parent Part', on_delete=django.db.models.deletion.CASCADE, related_name='parameters', to='part.Part', verbose_name='Part'), - ), - migrations.AlterField( - model_name='partparameter', - name='template', - field=models.ForeignKey(help_text='Parameter Template', on_delete=django.db.models.deletion.CASCADE, related_name='instances', to='part.PartParameterTemplate', verbose_name='Template'), - ), - migrations.AlterField( - model_name='partparametertemplate', - name='name', - field=models.CharField(help_text='Parameter Name', max_length=100, unique=True, verbose_name='Name'), - ), - migrations.AlterField( - model_name='partparametertemplate', - name='units', - field=models.CharField(blank=True, help_text='Parameter Units', max_length=25, verbose_name='Units'), - ), - migrations.AlterField( - model_name='partrelated', - name='part_1', - field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='related_parts_1', to='part.Part', verbose_name='Part 1'), - ), - migrations.AlterField( - model_name='partrelated', - name='part_2', - field=models.ForeignKey(help_text='Select Related Part', on_delete=django.db.models.deletion.DO_NOTHING, related_name='related_parts_2', to='part.Part', verbose_name='Part 2'), - ), - migrations.AlterField( - model_name='partsellpricebreak', - name='part', - field=models.ForeignKey(limit_choices_to={'salable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='salepricebreaks', to='part.Part', verbose_name='Part'), - ), - migrations.AlterField( - model_name='partstar', - name='part', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='starred_users', to='part.Part', verbose_name='Part'), - ), - migrations.AlterField( - model_name='partstar', - name='user', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='starred_parts', to=settings.AUTH_USER_MODEL, verbose_name='User'), - ), - migrations.AlterField( - model_name='parttesttemplate', - name='part', - field=models.ForeignKey(limit_choices_to={'trackable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='test_templates', to='part.Part', verbose_name='Part'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0065_auto_20210505_2144.py b/src/backend/InvenTree/part/migrations/0065_auto_20210505_2144.py deleted file mode 100644 index 328ce1f588..0000000000 --- a/src/backend/InvenTree/part/migrations/0065_auto_20210505_2144.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 3.2 on 2021-05-05 21:44 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0064_auto_20210404_2016'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='base_cost', - field=models.DecimalField(decimal_places=3, default=0, help_text='Minimum charge (e.g. stocking fee)', max_digits=10, validators=[django.core.validators.MinValueValidator(0)], verbose_name='base cost'), - ), - migrations.AddField( - model_name='part', - name='multiple', - field=models.PositiveIntegerField(default=1, help_text='Sell multiple', validators=[django.core.validators.MinValueValidator(1)], verbose_name='multiple'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0066_bomitem_allow_variants.py b/src/backend/InvenTree/part/migrations/0066_bomitem_allow_variants.py deleted file mode 100644 index e545c8e3cb..0000000000 --- a/src/backend/InvenTree/part/migrations/0066_bomitem_allow_variants.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2 on 2021-06-01 03:58 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0065_auto_20210505_2144'), - ] - - operations = [ - migrations.AddField( - model_name='bomitem', - name='allow_variants', - field=models.BooleanField(default=False, help_text='Stock items for variant parts can be used for this BOM item', verbose_name='Allow Variants'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0067_partinternalpricebreak.py b/src/backend/InvenTree/part/migrations/0067_partinternalpricebreak.py deleted file mode 100644 index b4e6be8f8c..0000000000 --- a/src/backend/InvenTree/part/migrations/0067_partinternalpricebreak.py +++ /dev/null @@ -1,32 +0,0 @@ -# Generated by Django 3.2 on 2021-06-05 14:13 - -import InvenTree.fields -import django.core.validators -import common.currency -from django.db import migrations, models -import django.db.models.deletion -import djmoney.models.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0066_bomitem_allow_variants'), - ] - - operations = [ - migrations.CreateModel( - name='PartInternalPriceBreak', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('quantity', InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Price break quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(1)], verbose_name='Quantity')), - ('price_currency', djmoney.models.fields.CurrencyField(choices=common.currency.currency_code_mappings(), default='', editable=False, max_length=3)), - ('price', djmoney.models.fields.MoneyField(decimal_places=4, default_currency='', help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price')), - ('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='internalpricebreaks', to='part.part', verbose_name='Part')), - ], - options={ - 'unique_together': {('part', 'quantity')}, - 'verbose_name': 'Part Internal Price Break', - }, - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0068_part_unique_part.py b/src/backend/InvenTree/part/migrations/0068_part_unique_part.py deleted file mode 100644 index 2e87fc7b2a..0000000000 --- a/src/backend/InvenTree/part/migrations/0068_part_unique_part.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.2.4 on 2021-06-21 23:10 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0067_partinternalpricebreak'), - ] - - operations = [ - migrations.AddConstraint( - model_name='part', - constraint=models.UniqueConstraint(fields=('name', 'IPN', 'revision'), name='unique_part'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0069_auto_20210701_0509.py b/src/backend/InvenTree/part/migrations/0069_auto_20210701_0509.py deleted file mode 100644 index 86d8d2d44c..0000000000 --- a/src/backend/InvenTree/part/migrations/0069_auto_20210701_0509.py +++ /dev/null @@ -1,35 +0,0 @@ -# Generated by Django 3.2.4 on 2021-07-01 05:09 - -import InvenTree.fields -from django.db import migrations -import djmoney.models.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0068_part_unique_part'), - ] - - operations = [ - migrations.AlterField( - model_name='partinternalpricebreak', - name='price', - field=InvenTree.fields.InvenTreeModelMoneyField(currency_choices=[], decimal_places=4, default_currency='', help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price'), - ), - migrations.AlterField( - model_name='partinternalpricebreak', - name='price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3), - ), - migrations.AlterField( - model_name='partsellpricebreak', - name='price', - field=InvenTree.fields.InvenTreeModelMoneyField(currency_choices=[], decimal_places=4, default_currency='', help_text='Unit price at specified quantity', max_digits=19, null=True, verbose_name='Price'), - ), - migrations.AlterField( - model_name='partsellpricebreak', - name='price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0070_alter_part_variant_of.py b/src/backend/InvenTree/part/migrations/0070_alter_part_variant_of.py deleted file mode 100644 index a2b2f7ec18..0000000000 --- a/src/backend/InvenTree/part/migrations/0070_alter_part_variant_of.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.4 on 2021-07-08 07:02 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0069_auto_20210701_0509'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='variant_of', - field=models.ForeignKey(blank=True, help_text='Is this part a variant of another part?', limit_choices_to={'is_template': True}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='variants', to='part.part', verbose_name='Variant Of'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0071_alter_partparametertemplate_name.py b/src/backend/InvenTree/part/migrations/0071_alter_partparametertemplate_name.py deleted file mode 100644 index 4fad4a9bf1..0000000000 --- a/src/backend/InvenTree/part/migrations/0071_alter_partparametertemplate_name.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.4 on 2021-08-07 11:40 - -from django.db import migrations, models -import part.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0070_alter_part_variant_of'), - ] - - operations = [ - migrations.AlterField( - model_name='partparametertemplate', - name='name', - field=models.CharField(help_text='Parameter Name', max_length=100, unique=True, validators=[], verbose_name='Name'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0072_bomitemsubstitute.py b/src/backend/InvenTree/part/migrations/0072_bomitemsubstitute.py deleted file mode 100644 index 2e48b10a8d..0000000000 --- a/src/backend/InvenTree/part/migrations/0072_bomitemsubstitute.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 3.2.5 on 2021-10-12 23:24 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0071_alter_partparametertemplate_name'), - ] - - operations = [ - migrations.CreateModel( - name='BomItemSubstitute', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('bom_item', models.ForeignKey(help_text='Parent BOM item', on_delete=django.db.models.deletion.CASCADE, related_name='substitutes', to='part.bomitem', verbose_name='BOM Item')), - ('part', models.ForeignKey(help_text='Substitute part', limit_choices_to={'component': True}, on_delete=django.db.models.deletion.CASCADE, related_name='substitute_items', to='part.part', verbose_name='Part')), - ], - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0073_auto_20211013_1048.py b/src/backend/InvenTree/part/migrations/0073_auto_20211013_1048.py deleted file mode 100644 index e581af603e..0000000000 --- a/src/backend/InvenTree/part/migrations/0073_auto_20211013_1048.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.2.5 on 2021-10-13 10:48 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0072_bomitemsubstitute'), - ] - - operations = [ - migrations.AlterModelOptions( - name='bomitemsubstitute', - options={'verbose_name': 'BOM Item Substitute'}, - ), - migrations.AlterUniqueTogether( - name='bomitemsubstitute', - unique_together={('part', 'bom_item')}, - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0074_partcategorystar.py b/src/backend/InvenTree/part/migrations/0074_partcategorystar.py deleted file mode 100644 index 0015212d2e..0000000000 --- a/src/backend/InvenTree/part/migrations/0074_partcategorystar.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 3.2.5 on 2021-11-03 07:03 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('part', '0073_auto_20211013_1048'), - ] - - operations = [ - migrations.CreateModel( - name='PartCategoryStar', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='starred_users', to='part.partcategory', verbose_name='Category')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='starred_categories', to=settings.AUTH_USER_MODEL, verbose_name='User')), - ], - options={ - 'unique_together': {('category', 'user')}, - }, - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0075_auto_20211128_0151.py b/src/backend/InvenTree/part/migrations/0075_auto_20211128_0151.py deleted file mode 100644 index f516846ae2..0000000000 --- a/src/backend/InvenTree/part/migrations/0075_auto_20211128_0151.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 3.2.5 on 2021-11-28 01:51 - -import InvenTree.fields -import InvenTree.models -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0074_partcategorystar'), - ] - - operations = [ - migrations.AddField( - model_name='partattachment', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', null=True, verbose_name='Link'), - ), - migrations.AlterField( - model_name='partattachment', - name='attachment', - field=models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to='attachments', verbose_name='Attachment'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0076_auto_20220516_0819.py b/src/backend/InvenTree/part/migrations/0076_auto_20220516_0819.py deleted file mode 100644 index 5b02860aca..0000000000 --- a/src/backend/InvenTree/part/migrations/0076_auto_20220516_0819.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.13 on 2022-05-16 08:19 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0075_auto_20211128_0151'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - migrations.AddField( - model_name='partcategory', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0077_alter_bomitem_unique_together.py b/src/backend/InvenTree/part/migrations/0077_alter_bomitem_unique_together.py deleted file mode 100644 index e12a402d84..0000000000 --- a/src/backend/InvenTree/part/migrations/0077_alter_bomitem_unique_together.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.2.13 on 2022-05-31 01:42 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0076_auto_20220516_0819'), - ] - - operations = [ - migrations.AlterUniqueTogether( - name='bomitem', - unique_together=set(), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0078_auto_20220606_0024.py b/src/backend/InvenTree/part/migrations/0078_auto_20220606_0024.py deleted file mode 100644 index 385eb591ce..0000000000 --- a/src/backend/InvenTree/part/migrations/0078_auto_20220606_0024.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-06 00:24 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0077_alter_bomitem_unique_together'), - ] - - operations = [ - migrations.AlterField( - model_name='partrelated', - name='part_1', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='related_parts_1', to='part.part', verbose_name='Part 1'), - ), - migrations.AlterField( - model_name='partrelated', - name='part_2', - field=models.ForeignKey(help_text='Select Related Part', on_delete=django.db.models.deletion.CASCADE, related_name='related_parts_2', to='part.part', verbose_name='Part 2'), - ), - migrations.AlterUniqueTogether( - name='partrelated', - unique_together={('part_1', 'part_2')}, - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0079_alter_part_notes.py b/src/backend/InvenTree/part/migrations/0079_alter_part_notes.py deleted file mode 100644 index 7d4cd5b80a..0000000000 --- a/src/backend/InvenTree/part/migrations/0079_alter_part_notes.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-20 07:28 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0078_auto_20220606_0024'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='notes', - field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Part notes', max_length=50000, null=True, verbose_name='Notes'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0080_alter_part_image.py b/src/backend/InvenTree/part/migrations/0080_alter_part_image.py deleted file mode 100644 index 9632795cc8..0000000000 --- a/src/backend/InvenTree/part/migrations/0080_alter_part_image.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-27 23:08 - -from django.db import migrations -import part.models -import stdimage.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0079_alter_part_notes'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='image', - field=stdimage.models.StdImageField(blank=True, force_min_size=False, null=True, upload_to=part.models.rename_part_image, variations={'preview': (256, 256), 'thumbnail': (128, 128)}, verbose_name='Image'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0081_alter_partcategory_name.py b/src/backend/InvenTree/part/migrations/0081_alter_partcategory_name.py deleted file mode 100644 index d91912980b..0000000000 --- a/src/backend/InvenTree/part/migrations/0081_alter_partcategory_name.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.14 on 2022-07-20 16:09 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0080_alter_part_image'), - ] - - operations = [ - migrations.AlterField( - model_name='partcategory', - name='name', - field=models.CharField(help_text='Name', max_length=100, verbose_name='Name'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0082_partcategory_pathstring.py b/src/backend/InvenTree/part/migrations/0082_partcategory_pathstring.py deleted file mode 100644 index 989c10f625..0000000000 --- a/src/backend/InvenTree/part/migrations/0082_partcategory_pathstring.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.14 on 2022-07-31 23:54 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0081_alter_partcategory_name'), - ] - - operations = [ - migrations.AddField( - model_name='partcategory', - name='pathstring', - field=models.CharField(blank=True, help_text='Path', max_length=250, verbose_name='Path'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0083_auto_20220731_2357.py b/src/backend/InvenTree/part/migrations/0083_auto_20220731_2357.py deleted file mode 100644 index 06980347e9..0000000000 --- a/src/backend/InvenTree/part/migrations/0083_auto_20220731_2357.py +++ /dev/null @@ -1,48 +0,0 @@ -# Generated by Django 3.2.14 on 2022-07-31 23:57 - -from django.db import migrations - -from InvenTree.helpers import constructPathString - - -def update_pathstring(apps, schema_editor): - """Construct pathstring for all existing PartCategory objects""" - - PartCategory = apps.get_model('part', 'partcategory') - - n = PartCategory.objects.count() - - if n > 0: - - for cat in PartCategory.objects.all(): - - # Construct complete path for category - path = [cat.name] - - parent = cat.parent - - # Iterate up the tree - while parent is not None: - path = [parent.name] + path - parent = parent.parent - - pathstring = constructPathString(path) - - cat.pathstring = pathstring - cat.save() - - print(f"\n--- Updated 'pathstring' for {n} PartCategory objects ---\n") - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0082_partcategory_pathstring'), - ] - - operations = [ - migrations.RunPython( - update_pathstring, - reverse_code=migrations.RunPython.noop - ) - ] diff --git a/src/backend/InvenTree/part/migrations/0084_partcategory_icon.py b/src/backend/InvenTree/part/migrations/0084_partcategory_icon.py deleted file mode 100644 index 8b92cfaad0..0000000000 --- a/src/backend/InvenTree/part/migrations/0084_partcategory_icon.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.15 on 2022-08-15 08:38 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0083_auto_20220731_2357'), - ] - - operations = [ - migrations.AddField( - model_name='partcategory', - name='icon', - field=models.CharField(blank=True, help_text='Icon (optional)', max_length=100, verbose_name='Icon'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0085_partparametertemplate_description.py b/src/backend/InvenTree/part/migrations/0085_partparametertemplate_description.py deleted file mode 100644 index 9e0103eeaf..0000000000 --- a/src/backend/InvenTree/part/migrations/0085_partparametertemplate_description.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.15 on 2022-08-24 12:09 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0084_partcategory_icon'), - ] - - operations = [ - migrations.AddField( - model_name='partparametertemplate', - name='description', - field=models.CharField(blank=True, help_text='Parameter description', max_length=250, verbose_name='Description'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0086_auto_20220912_0007.py b/src/backend/InvenTree/part/migrations/0086_auto_20220912_0007.py deleted file mode 100644 index dfaba36cf2..0000000000 --- a/src/backend/InvenTree/part/migrations/0086_auto_20220912_0007.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.15 on 2022-09-12 00:07 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0085_partparametertemplate_description'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='barcode_data', - field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), - ), - migrations.AddField( - model_name='part', - name='barcode_hash', - field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0087_bomitem_consumable.py b/src/backend/InvenTree/part/migrations/0087_bomitem_consumable.py deleted file mode 100644 index 79c793c99a..0000000000 --- a/src/backend/InvenTree/part/migrations/0087_bomitem_consumable.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.13 on 2022-04-28 00:08 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0086_auto_20220912_0007'), - ] - - operations = [ - migrations.AddField( - model_name='bomitem', - name='consumable', - field=models.BooleanField(default=False, help_text='This BOM item is consumable (it is not tracked in build orders)', verbose_name='Consumable'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0088_alter_partparametertemplate_name.py b/src/backend/InvenTree/part/migrations/0088_alter_partparametertemplate_name.py deleted file mode 100644 index b00679820c..0000000000 --- a/src/backend/InvenTree/part/migrations/0088_alter_partparametertemplate_name.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.16 on 2022-10-30 23:06 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0087_bomitem_consumable'), - ] - - operations = [ - migrations.AlterField( - model_name='partparametertemplate', - name='name', - field=models.CharField(help_text='Parameter Name', max_length=100, unique=True, verbose_name='Name'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0089_auto_20221112_0128.py b/src/backend/InvenTree/part/migrations/0089_auto_20221112_0128.py deleted file mode 100644 index 57cb415707..0000000000 --- a/src/backend/InvenTree/part/migrations/0089_auto_20221112_0128.py +++ /dev/null @@ -1,77 +0,0 @@ -# Generated by Django 3.2.16 on 2022-11-12 01:28 - -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion -import djmoney.models.fields -import djmoney.models.validators - -import InvenTree.fields -import common.currency - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0088_alter_partparametertemplate_name'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='base_cost', - field=models.DecimalField(decimal_places=6, default=0, help_text='Minimum charge (e.g. stocking fee)', max_digits=19, validators=[django.core.validators.MinValueValidator(0)], verbose_name='base cost'), - ), - migrations.AlterField( - model_name='partinternalpricebreak', - name='price', - field=InvenTree.fields.InvenTreeModelMoneyField(currency_choices=[], decimal_places=6, default_currency='', help_text='Unit price at specified quantity', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price'), - ), - migrations.AlterField( - model_name='partsellpricebreak', - name='price', - field=InvenTree.fields.InvenTreeModelMoneyField(currency_choices=[], decimal_places=6, default_currency='', help_text='Unit price at specified quantity', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Price'), - ), - migrations.CreateModel( - name='PartPricing', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('currency', models.CharField(choices=common.currency.currency_code_mappings(), default=common.currency.currency_code_default, help_text='Currency used to cache pricing calculations', max_length=10, verbose_name='Currency')), - ('updated', models.DateTimeField(auto_now=True, help_text='Timestamp of last pricing update', verbose_name='Updated')), - ('scheduled_for_update', models.BooleanField(default=False)), - ('bom_cost_min_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('bom_cost_min', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Minimum cost of component parts', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum BOM Cost')), - ('bom_cost_max_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('bom_cost_max', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Maximum cost of component parts', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum BOM Cost')), - ('purchase_cost_min_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('purchase_cost_min', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Minimum historical purchase cost', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum Purchase Cost')), - ('purchase_cost_max_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('purchase_cost_max', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Maximum historical purchase cost', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum Purchase Cost')), - ('internal_cost_min_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('internal_cost_min', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Minimum cost based on internal price breaks', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum Internal Price')), - ('internal_cost_max_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('internal_cost_max', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Maximum cost based on internal price breaks', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum Internal Price')), - ('supplier_price_min_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('supplier_price_min', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Minimum price of part from external suppliers', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum Supplier Price')), - ('supplier_price_max_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('supplier_price_max', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Maximum price of part from external suppliers', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum Supplier Price')), - ('variant_cost_min_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('variant_cost_min', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Calculated minimum cost of variant parts', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum Variant Cost')), - ('variant_cost_max_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('variant_cost_max', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Calculated maximum cost of variant parts', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum Variant Cost')), - ('overall_min_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('overall_min', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Calculated overall minimum cost', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum Cost')), - ('overall_max_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('overall_max', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Calculated overall maximum cost', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum Cost')), - ('sale_price_min_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('sale_price_min', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Minimum sale price based on price breaks', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum Sale Price')), - ('sale_price_max_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('sale_price_max', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Maximum sale price based on price breaks', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum Sale Price')), - ('sale_history_min_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('sale_history_min', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Minimum historical sale price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum Sale Cost')), - ('sale_history_max_currency', djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3)), - ('sale_history_max', InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Maximum historical sale price', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum Sale Cost')), - ('part', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='pricing_data', to='part.part', verbose_name='Part')), - ], - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0090_auto_20221115_0816.py b/src/backend/InvenTree/part/migrations/0090_auto_20221115_0816.py deleted file mode 100644 index 42ba282bf0..0000000000 --- a/src/backend/InvenTree/part/migrations/0090_auto_20221115_0816.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.16 on 2022-11-15 08:16 - -import common.settings -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0089_auto_20221112_0128'), - ] - - operations = [ - migrations.AddField( - model_name='partcategory', - name='structural', - field=models.BooleanField(default=False, help_text="Parts may not be directly assigned to a structural category, but may be assigned to child categories.", verbose_name='Structural'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0091_partstocktake.py b/src/backend/InvenTree/part/migrations/0091_partstocktake.py deleted file mode 100644 index 948ee48b59..0000000000 --- a/src/backend/InvenTree/part/migrations/0091_partstocktake.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 3.2.16 on 2022-12-21 11:26 - -from django.conf import settings -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('part', '0090_auto_20221115_0816'), - ] - - operations = [ - migrations.CreateModel( - name='PartStocktake', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('quantity', models.DecimalField(decimal_places=5, help_text='Total available stock at time of stocktake', max_digits=19, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity')), - ('date', models.DateField(auto_now_add=True, help_text='Date stocktake was performed', verbose_name='Date')), - ('note', models.CharField(blank=True, help_text='Additional notes', max_length=250, verbose_name='Notes')), - ('part', models.ForeignKey(help_text='Part for stocktake', on_delete=django.db.models.deletion.CASCADE, related_name='stocktakes', to='part.part', verbose_name='Part')), - ('user', models.ForeignKey(blank=True, help_text='User who performed this stocktake', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='part_stocktakes', to=settings.AUTH_USER_MODEL, verbose_name='User')), - ], - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0092_part_last_stocktake.py b/src/backend/InvenTree/part/migrations/0092_part_last_stocktake.py deleted file mode 100644 index d5035e4716..0000000000 --- a/src/backend/InvenTree/part/migrations/0092_part_last_stocktake.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.16 on 2022-12-31 09:51 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0091_partstocktake'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='last_stocktake', - field=models.DateField(blank=True, null=True, verbose_name='Last Stocktake'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0093_auto_20230115_1404.py b/src/backend/InvenTree/part/migrations/0093_auto_20230115_1404.py deleted file mode 100644 index 9fbd5aee12..0000000000 --- a/src/backend/InvenTree/part/migrations/0093_auto_20230115_1404.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 3.2.16 on 2023-01-15 14:04 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0092_part_last_stocktake'), - ] - - operations = [ - migrations.AddField( - model_name='partinternalpricebreak', - name='updated', - field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'), - ), - migrations.AddField( - model_name='partsellpricebreak', - name='updated', - field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'), - ), - migrations.AlterField( - model_name='partpricing', - name='updated', - field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0094_alter_part_units.py b/src/backend/InvenTree/part/migrations/0094_alter_part_units.py deleted file mode 100644 index fc08e18292..0000000000 --- a/src/backend/InvenTree/part/migrations/0094_alter_part_units.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.16 on 2023-01-25 02:06 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0093_auto_20230115_1404'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='units', - field=models.CharField(blank=True, default='', help_text='Units of measure for this part', max_length=20, null=True, verbose_name='Units'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0095_alter_part_responsible.py b/src/backend/InvenTree/part/migrations/0095_alter_part_responsible.py deleted file mode 100644 index 11ffd3a535..0000000000 --- a/src/backend/InvenTree/part/migrations/0095_alter_part_responsible.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 3.2.16 on 2023-02-09 23:52 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('part', '0094_alter_part_units'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='responsible', - field=models.ForeignKey(blank=True, help_text='User responsible for this part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parts_responible', to=settings.AUTH_USER_MODEL, verbose_name='Responsible'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0096_auto_20230211_0029.py b/src/backend/InvenTree/part/migrations/0096_auto_20230211_0029.py deleted file mode 100644 index dce4afcdd8..0000000000 --- a/src/backend/InvenTree/part/migrations/0096_auto_20230211_0029.py +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Django 3.2.16 on 2023-02-11 00:29 - -import InvenTree.fields -from django.db import migrations -import djmoney.models.fields -import djmoney.models.validators - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0095_alter_part_responsible'), - ] - - operations = [ - migrations.AddField( - model_name='partstocktake', - name='cost_max', - field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Estimated maximum cost of stock on hand', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum Stock Cost'), - ), - migrations.AddField( - model_name='partstocktake', - name='cost_max_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3), - ), - migrations.AddField( - model_name='partstocktake', - name='cost_min', - field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Estimated minimum cost of stock on hand', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum Stock Cost'), - ), - migrations.AddField( - model_name='partstocktake', - name='cost_min_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0097_partstocktakereport.py b/src/backend/InvenTree/part/migrations/0097_partstocktakereport.py deleted file mode 100644 index 330776b7ab..0000000000 --- a/src/backend/InvenTree/part/migrations/0097_partstocktakereport.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 3.2.16 on 2023-02-12 08:29 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -def fake_func(*args, **kwargs): - """A placeholder function to avoid import errors.""" - pass - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('part', '0096_auto_20230211_0029'), - ] - - operations = [ - migrations.CreateModel( - name='PartStocktakeReport', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('date', models.DateField(auto_now_add=True, verbose_name='Date')), - ('report', models.FileField(help_text='Stocktake report file (generated internally)', upload_to=fake_func, verbose_name='Report')), - ('user', models.ForeignKey(blank=True, help_text='User who requested this stocktake report', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stocktake_reports', to=settings.AUTH_USER_MODEL, verbose_name='User')), - ], - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0098_auto_20230214_1115.py b/src/backend/InvenTree/part/migrations/0098_auto_20230214_1115.py deleted file mode 100644 index dd28185fee..0000000000 --- a/src/backend/InvenTree/part/migrations/0098_auto_20230214_1115.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.16 on 2023-02-14 11:15 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0097_partstocktakereport'), - ] - - operations = [ - migrations.AddField( - model_name='partstocktake', - name='item_count', - field=models.IntegerField(default=1, help_text='Number of individual stock entries at time of stocktake', verbose_name='Item Count'), - ), - migrations.AddField( - model_name='partstocktakereport', - name='part_count', - field=models.IntegerField(default=0, help_text='Number of parts covered by stocktake', verbose_name='Part Count'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0099_alter_bomitem_inherited.py b/src/backend/InvenTree/part/migrations/0099_alter_bomitem_inherited.py deleted file mode 100644 index f424e13143..0000000000 --- a/src/backend/InvenTree/part/migrations/0099_alter_bomitem_inherited.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.18 on 2023-02-23 11:53 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0098_auto_20230214_1115'), - ] - - operations = [ - migrations.AlterField( - model_name='bomitem', - name='inherited', - field=models.BooleanField(default=False, help_text='This BOM item is inherited by BOMs for variant parts', verbose_name='Gets inherited'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0100_alter_bomitem_reference.py b/src/backend/InvenTree/part/migrations/0100_alter_bomitem_reference.py deleted file mode 100644 index 8e403125e0..0000000000 --- a/src/backend/InvenTree/part/migrations/0100_alter_bomitem_reference.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-08 21:00 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0099_alter_bomitem_inherited'), - ] - - operations = [ - migrations.AlterField( - model_name='bomitem', - name='reference', - field=models.CharField(blank=True, help_text='BOM item reference', max_length=5000, verbose_name='Reference'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0101_bomitem_validated.py b/src/backend/InvenTree/part/migrations/0101_bomitem_validated.py deleted file mode 100644 index 977095e287..0000000000 --- a/src/backend/InvenTree/part/migrations/0101_bomitem_validated.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-14 01:08 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0100_alter_bomitem_reference'), - ] - - operations = [ - migrations.AddField( - model_name='bomitem', - name='validated', - field=models.BooleanField(default=False, help_text='This BOM item has been validated', verbose_name='Validated'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0102_auto_20230314_0112.py b/src/backend/InvenTree/part/migrations/0102_auto_20230314_0112.py deleted file mode 100644 index 220e14c1e0..0000000000 --- a/src/backend/InvenTree/part/migrations/0102_auto_20230314_0112.py +++ /dev/null @@ -1,108 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-14 01:12 - -import hashlib -import logging - -from django.db import migrations -from jinja2 import Template - -from InvenTree.helpers import normalize - - -logger = logging.getLogger('inventree') - - -def update_bom_item(apps, schema_editor): - """Update all existing BomItem instances, and cache the 'validated' field. - - The 'validated' field denotes whether this individual BomItem has been validated, - which previously was calculated on the fly (which was very expensive). - """ - - BomItem = apps.get_model('part', 'bomitem') - InvenTreeSetting = apps.get_model('common', 'inventreesetting') - - n = BomItem.objects.count() - - if n > 0: - - for item in BomItem.objects.all(): - """For each item, we need to re-calculate the "checksum", based on the *old* routine. - Note that as we cannot access the ORM models, we have to do this "by hand" - """ - - # Construct the 'full_name' for the sub_part (this is no longer required, but *was* required at point of migration) - try: - setting = InvenTreeSetting.objects.get(key='PART_NAME_FORMAT') - full_name_pattern = str(setting.value) - except Exception: - full_name_pattern = "{{ part.IPN if part.IPN }}{{ ' | ' if part.IPN }}{{ part.name }}{{ ' | ' if part.revision }}{{ part.revision if part.revision }}" - - template = Template(full_name_pattern) - - full_name = template.render({'part': item.sub_part}) - - # Calculate the OLD checksum manually for this BomItem - old_hash = hashlib.md5(str(item.pk).encode()) - old_hash.update(str(item.sub_part.pk).encode()) - old_hash.update(str(full_name).encode()) - old_hash.update(str(item.quantity).encode()) - old_hash.update(str(item.note).encode()) - old_hash.update(str(item.reference).encode()) - old_hash.update(str(item.optional).encode()) - old_hash.update(str(item.inherited).encode()) - - if item.consumable: - old_hash.update(str(item.consumable).encode()) - - if item.allow_variants: - old_hash.update(str(item.allow_variants).encode()) - - checksum = str(old_hash.digest()) - - # Now, update the 'validated' field based on whether the checksum is 'valid' or not - item.validated = item.checksum == checksum - - """Next, we need to update the item with a "new" hash, with the following differences: - - Uses the PK of the 'part', not the BomItem itself, - - Does not use the 'full_name' of the linked 'sub_part' - - Does not use the 'note' field - """ - - if item.validated: - - new_hash = hashlib.md5(''.encode()) - - components = [ - item.part.pk, - item.sub_part.pk, - normalize(item.quantity), - item.reference, - item.optional, - item.inherited, - item.consumable, - item.allow_variants - ] - - for component in components: - new_hash.update(str(component).encode()) - - item.checksum = str(new_hash.digest()) - - item.save() - - logger.info(f"Updated 'validated' flag for {n} BomItem objects") - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0101_bomitem_validated'), - ] - - operations = [ - migrations.RunPython( - update_bom_item, - reverse_code=migrations.RunPython.noop - ) - ] diff --git a/src/backend/InvenTree/part/migrations/0103_auto_20230317_0816.py b/src/backend/InvenTree/part/migrations/0103_auto_20230317_0816.py deleted file mode 100644 index 4259c9b520..0000000000 --- a/src/backend/InvenTree/part/migrations/0103_auto_20230317_0816.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-17 08:16 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0102_auto_20230314_0112'), - ] - - operations = [ - migrations.AddField( - model_name='bomitem', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - migrations.AddField( - model_name='partparametertemplate', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0104_alter_part_description.py b/src/backend/InvenTree/part/migrations/0104_alter_part_description.py deleted file mode 100644 index 750e21f46b..0000000000 --- a/src/backend/InvenTree/part/migrations/0104_alter_part_description.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-12 17:52 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0103_auto_20230317_0816'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='description', - field=models.CharField(blank=True, help_text='Part description (optional)', max_length=250, verbose_name='Description'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0105_alter_part_notes.py b/src/backend/InvenTree/part/migrations/0105_alter_part_notes.py deleted file mode 100644 index 081ff7b6ef..0000000000 --- a/src/backend/InvenTree/part/migrations/0105_alter_part_notes.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-19 00:37 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0104_alter_part_description'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='notes', - field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0106_part_tags.py b/src/backend/InvenTree/part/migrations/0106_part_tags.py deleted file mode 100644 index b4a6badea2..0000000000 --- a/src/backend/InvenTree/part/migrations/0106_part_tags.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-27 20:33 - -from django.db import migrations -import taggit.managers - - -class Migration(migrations.Migration): - - dependencies = [ - ('taggit', '0005_auto_20220424_2025'), - ('part', '0105_alter_part_notes'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='tags', - field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0107_alter_part_tags.py b/src/backend/InvenTree/part/migrations/0107_alter_part_tags.py deleted file mode 100644 index 15ce10f530..0000000000 --- a/src/backend/InvenTree/part/migrations/0107_alter_part_tags.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-15 00:04 - -from django.db import migrations -import taggit.managers - - -class Migration(migrations.Migration): - - dependencies = [ - ('taggit', '0005_auto_20220424_2025'), - ('part', '0106_part_tags'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='tags', - field=taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0108_auto_20230516_1334.py b/src/backend/InvenTree/part/migrations/0108_auto_20230516_1334.py deleted file mode 100644 index 1bbef6aa1b..0000000000 --- a/src/backend/InvenTree/part/migrations/0108_auto_20230516_1334.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-16 13:34 - -import InvenTree.validators -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0107_alter_part_tags'), - ] - - operations = [ - migrations.AddField( - model_name='partparameter', - name='data_numeric', - field=models.FloatField(default=None, null=True, blank=True), - ), - migrations.AlterField( - model_name='partparameter', - name='data', - field=models.CharField(help_text='Parameter Value', max_length=500, validators=[django.core.validators.MinLengthValidator(1)], verbose_name='Data'), - ), - migrations.AlterField( - model_name='partparametertemplate', - name='units', - field=models.CharField(blank=True, help_text='Physical units for this parameter', max_length=25, validators=[InvenTree.validators.validate_physical_units], verbose_name='Units'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0109_auto_20230517_1048.py b/src/backend/InvenTree/part/migrations/0109_auto_20230517_1048.py deleted file mode 100644 index d2ef071050..0000000000 --- a/src/backend/InvenTree/part/migrations/0109_auto_20230517_1048.py +++ /dev/null @@ -1,156 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-17 10:48 - -import pint - -from django.core.exceptions import ValidationError -from django.db import migrations - -import InvenTree.conversion - - -def update_template_units(apps, schema_editor): - """Update the units for each parameter template: - - - Check if the units are valid - - Attempt to convert to valid units (if possible) - """ - - PartParameterTemplate = apps.get_model('part', 'PartParameterTemplate') - - n_templates = PartParameterTemplate.objects.count() - - if n_templates == 0: - # Escape early - return - - ureg = InvenTree.conversion.get_unit_registry() - - n_converted = 0 - invalid_units = set() - - for template in PartParameterTemplate.objects.all(): - - # Skip empty units - if not template.units: - continue - - # Override '%' units (which are invalid) - if template.units == '%': - template.units = 'percent' - template.save() - n_converted += 1 - continue - - # Test if unit is 'valid' - try: - ureg.Unit(template.units) - continue - except Exception: - pass - - # Check a lower-case version - try: - ureg.Unit(template.units.lower()) - print(f"Found unit match: {template.units} -> {template.units.lower()}") - template.units = template.units.lower() - template.save() - n_converted += 1 - continue - except Exception: - pass - - found = False - - # Attempt to convert to a valid unit - # Look for capitalization issues (e.g. "Ohm" -> "ohm") - for unit in ureg: - if unit.lower() == template.units.lower(): - print(f"Found unit match: {template.units} -> {unit}") - template.units = str(unit) - template.save() - n_converted += 1 - found = True - break - - if not found: - print(f"warning: Could not find unit match for {template.units}") - invalid_units.add(template.units) - - print(f"Updated units for {n_templates} parameter templates") - - if n_converted > 0: - print(f" - Converted {n_converted} units") - - if len(invalid_units) > 0: - print(f" - Found {len(invalid_units)} invalid units:") - - for unit in invalid_units: - print(f" - {unit}") - - - -def convert_to_numeric_value(value: str, units: str): - """Convert a value (with units) to a numeric value. - - Defaults to zero if the value cannot be converted. - """ - - # Default value is null - result = None - - if units: - try: - result = InvenTree.conversion.convert_physical_value(value, units) - result = float(result.magnitude) - except Exception: - pass - else: - try: - result = float(value) - except Exception: - pass - - return result - - -def update_parameter_values(apps, schema_editor): - """Update the parameter values for all parts: - - - Calculate the 'data_numeric' value for each parameter - - If the template has invalid units, we'll ignore - """ - - PartParameter = apps.get_model('part', 'PartParameter') - - n_params = PartParameter.objects.count() - - # Convert each parameter value to a the specified units - for parameter in PartParameter.objects.all(): - try: - parameter.data_numeric = convert_to_numeric_value(parameter.data, parameter.template.units) - parameter.save() - except Exception: - pass - - if n_params > 0: - print(f"Updated {n_params} parameter values") - - -class Migration(migrations.Migration): - - atomic = False - - dependencies = [ - ('part', '0108_auto_20230516_1334'), - ] - - operations = [ - migrations.RunPython( - update_template_units, - reverse_code=migrations.RunPython.noop - ), - migrations.RunPython( - update_parameter_values, - reverse_code=migrations.RunPython.noop - ) - ] diff --git a/src/backend/InvenTree/part/migrations/0110_alter_part_units.py b/src/backend/InvenTree/part/migrations/0110_alter_part_units.py deleted file mode 100644 index 4ddad84858..0000000000 --- a/src/backend/InvenTree/part/migrations/0110_alter_part_units.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-19 03:31 - -import InvenTree.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0109_auto_20230517_1048'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='units', - field=models.CharField(blank=True, default='', help_text='Units of measure for this part', max_length=20, null=True, validators=[InvenTree.validators.validate_physical_units], verbose_name='Units'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0111_auto_20230521_1350.py b/src/backend/InvenTree/part/migrations/0111_auto_20230521_1350.py deleted file mode 100644 index 4ac64c9011..0000000000 --- a/src/backend/InvenTree/part/migrations/0111_auto_20230521_1350.py +++ /dev/null @@ -1,95 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-21 13:50 - -import pint - -from django.core.exceptions import ValidationError -from django.db import migrations - -import InvenTree.conversion - - -def migrate_part_units(apps, schema_editor): - """Update the units field for each Part object: - - - Check if the units are valid - - Attempt to convert to valid units (if possible) - """ - - Part = apps.get_model('part', 'Part') - - parts = Part.objects.exclude(units=None).exclude(units='') - n_parts = parts.count() - - if n_parts == 0: - # Escape early - return - - ureg = InvenTree.conversion.get_unit_registry() - - invalid_units = set() - n_converted = 0 - - for part in parts: - - # Override '%' units (which are invalid) - if part.units == '%': - part.units = 'percent' - part.save() - continue - - # Test if unit is 'valid' - try: - ureg.Unit(part.units) - continue - except Exception: - pass - - # Check a lower-case version - try: - ureg.Unit(part.units.lower()) - print(f"Found unit match: {part.units} -> {part.units.lower()}") - part.units = part.units.lower() - part.save() - n_converted += 1 - continue - except Exception: - pass - - found = False - - # Attempt to convert to a valid unit - for unit in ureg: - if unit.lower() == part.units.lower(): - print("Found unit match: {part.units} -> {unit}") - part.units = str(unit) - part.save() - n_converted += 1 - found = True - break - - if not found: - print(f"Warning: Invalid units for part '{part}': {part.units}") - invalid_units.add(part.units) - - print(f"Updated units for {n_parts} parts") - - if n_converted > 0: - print(f"Converted units for {n_converted} parts") - - if len(invalid_units) > 0: - print(f"Found {len(invalid_units)} invalid units:") - for unit in invalid_units: - print(f" - {unit}") - - -class Migration(migrations.Migration): - - atomic = False - - dependencies = [ - ('part', '0110_alter_part_units'), - ] - - operations = [ - migrations.RunPython(code=migrate_part_units, reverse_code=migrations.RunPython.noop) - ] diff --git a/src/backend/InvenTree/part/migrations/0112_auto_20230525_1606.py b/src/backend/InvenTree/part/migrations/0112_auto_20230525_1606.py deleted file mode 100644 index c1aebf3866..0000000000 --- a/src/backend/InvenTree/part/migrations/0112_auto_20230525_1606.py +++ /dev/null @@ -1,97 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-31 12:05 - -""" -Note: This is a total hack method to delete columns (if they already exist). - -Due to an improper set of migrations merges, -there may exist a situation where the columns (defined in the migrations below) already exist. - -In this case, we want to delete the columns, and then re-add them. - -Original error: https://github.com/inventree/InvenTree/pull/4898 -1st fix: https://github.com/inventree/InvenTree/pull/4961 -2nd fix: https://github.com/inventree/InvenTree/pull/4977 -3rd fix: https://github.com/inventree/InvenTree/pull/4987 -""" - -from django.db import migrations, models - - -class RemoveFieldOrSkip(migrations.RemoveField): - """Custom RemoveField operation which will fail gracefully if the field does not exist - - Ref: https://stackoverflow.com/questions/58518726/how-to-ignore-a-specific-migration - """ - - def database_backwards(self, app_label, schema_editor, from_state, to_state) -> None: - # Backwards migration should not do anything - pass - - def database_forwards(self, app_label, schema_editor, from_state, to_state) -> None: - """Forwards migration *attempts* to remove existing fields, but will fail gracefully if they do not exist""" - - try: - super().database_forwards(app_label, schema_editor, from_state, to_state) - print(f'Removed field {self.name} from model {self.model_name}') - except Exception as exc: - pass - - def state_forwards(self, app_label, state) -> None: - try: - super().state_forwards(app_label, state) - except Exception: - pass - -class AddFieldOrSkip(migrations.AddField): - """Custom AddField operation which will fail gracefully if the field already exists - - Ref: https://stackoverflow.com/questions/58518726/how-to-ignore-a-specific-migration - """ - - def database_backwards(self, app_label, schema_editor, from_state, to_state) -> None: - # Backwards migration should not do anything - pass - - def database_forwards(self, app_label, schema_editor, from_state, to_state) -> None: - """Forwards migration *attempts* to remove existing fields, but will fail gracefully if they do not exist""" - - try: - super().database_forwards(app_label, schema_editor, from_state, to_state) - except Exception as exc: - pass - - def state_forwards(self, app_label, state) -> None: - try: - super().state_forwards(app_label, state) - except Exception: - pass - - -class Migration(migrations.Migration): - - atomic = False - - dependencies = [ - ('part', '0111_auto_20230521_1350'), - ] - - operations = [ - RemoveFieldOrSkip( - model_name='partparametertemplate', - name='checkbox', - ), - RemoveFieldOrSkip( - model_name='partparametertemplate', - name='choices', - ), - AddFieldOrSkip( - model_name='partparametertemplate', - name='checkbox', - field=models.BooleanField(default=False, help_text='Is this parameter a checkbox?', verbose_name='Checkbox'), - ), - AddFieldOrSkip( - model_name='partparametertemplate', - name='choices', - field=models.CharField(blank=True, help_text='Valid choices for this parameter (comma-separated)', max_length=5000, verbose_name='Choices'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0113_auto_20230531_1205.py b/src/backend/InvenTree/part/migrations/0113_auto_20230531_1205.py deleted file mode 100644 index 576e2e5319..0000000000 --- a/src/backend/InvenTree/part/migrations/0113_auto_20230531_1205.py +++ /dev/null @@ -1,38 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-25 16:06 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0112_auto_20230525_1606'), - ] - - operations = [ - migrations.AddField( - model_name='bomitemsubstitute', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - migrations.AddField( - model_name='partcategoryparametertemplate', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - migrations.AddField( - model_name='partparameter', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - migrations.AddField( - model_name='partrelated', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - migrations.AddField( - model_name='parttesttemplate', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0114_alter_part_minimum_stock.py b/src/backend/InvenTree/part/migrations/0114_alter_part_minimum_stock.py deleted file mode 100644 index 2845504b0a..0000000000 --- a/src/backend/InvenTree/part/migrations/0114_alter_part_minimum_stock.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.21 on 2023-09-24 13:27 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0113_auto_20230531_1205'), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='minimum_stock', - field=models.DecimalField(decimal_places=6, default=0, help_text='Minimum allowed stock level', max_digits=19, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Minimum Stock'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0115_part_responsible_owner.py b/src/backend/InvenTree/part/migrations/0115_part_responsible_owner.py deleted file mode 100644 index 2605ac51f2..0000000000 --- a/src/backend/InvenTree/part/migrations/0115_part_responsible_owner.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.22 on 2023-10-23 01:59 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0009_auto_20231020_2356'), - ('part', '0114_alter_part_minimum_stock'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='responsible_owner', - field=models.ForeignKey(blank=True, help_text='Owner responsible for this part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parts_responsible', to='users.owner', verbose_name='Responsible'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0116_auto_20231023_0332.py b/src/backend/InvenTree/part/migrations/0116_auto_20231023_0332.py deleted file mode 100644 index 49f3f675a9..0000000000 --- a/src/backend/InvenTree/part/migrations/0116_auto_20231023_0332.py +++ /dev/null @@ -1,76 +0,0 @@ -# Generated by Django 3.2.22 on 2023-10-23 03:32 - -from django.db import migrations - - -def migrate_part_responsible_owner(apps, schema_editor): - """Copy existing part.responsible field to part.responsible_owner""" - - Owner = apps.get_model('users', 'Owner') - Part = apps.get_model('part', 'Part') - User = apps.get_model('auth', 'user') - ContentType = apps.get_model('contenttypes', 'contenttype') - - user_type = ContentType.objects.get_for_model(User) - - parts = Part.objects.exclude(responsible=None) - - for part in parts: - - # Find a corresponding Owner object, or create one if it does not exist - owner, _created = Owner.objects.get_or_create( - owner_type=user_type, - owner_id=part.responsible.id, - ) - - part.responsible_owner = owner - part.save() - - if parts.count() > 0: - print(f"Added 'responsible_owner' for {parts.count()} parts") - - -def reverse_owner_migration(apps, schema_editor): - """Reverse the owner migration: - - - Set the 'responsible' field to a selected user - - Only where 'responsible_owner' is set - - Only where 'responsible_owner' is a User object - """ - - Part = apps.get_model('part', 'Part') - User = apps.get_model('auth', 'user') - ContentType = apps.get_model('contenttypes', 'contenttype') - - user_type = ContentType.objects.get_for_model(User) - - parts = Part.objects.exclude(responsible_owner=None) - - for part in parts: - - if part.responsible_owner.owner_type == user_type: - - # Attempt to find matching user - try: - user = User.objects.get(pk=part.responsible_owner.owner_id) - part.responsible = user - part.save() - except User.DoesNotExist: - print("User does not exist:", part.responsible_owner.owner_id) - - if parts.count() > 0: - print(f"Added 'responsible' for {parts.count()} parts") - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0115_part_responsible_owner'), - ('users', '0005_owner_model'), - ] - - operations = [ - migrations.RunPython( - migrate_part_responsible_owner, - reverse_code=reverse_owner_migration, - ) - ] diff --git a/src/backend/InvenTree/part/migrations/0117_remove_part_responsible.py b/src/backend/InvenTree/part/migrations/0117_remove_part_responsible.py deleted file mode 100644 index add8405179..0000000000 --- a/src/backend/InvenTree/part/migrations/0117_remove_part_responsible.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.2.22 on 2023-10-23 05:28 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0116_auto_20231023_0332'), - ] - - operations = [ - migrations.RemoveField( - model_name='part', - name='responsible', - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0118_auto_20231024_1844.py b/src/backend/InvenTree/part/migrations/0118_auto_20231024_1844.py deleted file mode 100644 index 6a29bf89d6..0000000000 --- a/src/backend/InvenTree/part/migrations/0118_auto_20231024_1844.py +++ /dev/null @@ -1,114 +0,0 @@ -# Generated by Django 3.2.22 on 2023-10-24 16:44 - -from django.db import migrations -import djmoney.models.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0117_remove_part_responsible'), - ] - - operations = [ - migrations.AlterField( - model_name='partinternalpricebreak', - name='price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partpricing', - name='bom_cost_max_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partpricing', - name='bom_cost_min_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partpricing', - name='internal_cost_max_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partpricing', - name='internal_cost_min_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partpricing', - name='overall_max_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partpricing', - name='overall_min_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partpricing', - name='purchase_cost_max_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partpricing', - name='purchase_cost_min_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partpricing', - name='sale_history_max_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partpricing', - name='sale_history_min_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partpricing', - name='sale_price_max_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partpricing', - name='sale_price_min_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partpricing', - name='supplier_price_max_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partpricing', - name='supplier_price_min_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partpricing', - name='variant_cost_max_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partpricing', - name='variant_cost_min_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partsellpricebreak', - name='price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partstocktake', - name='cost_max_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AlterField( - model_name='partstocktake', - name='cost_min_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0119_auto_20231120_0457.py b/src/backend/InvenTree/part/migrations/0119_auto_20231120_0457.py deleted file mode 100644 index f63ccc70db..0000000000 --- a/src/backend/InvenTree/part/migrations/0119_auto_20231120_0457.py +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Django 3.2.23 on 2023-11-20 04:57 - -import InvenTree.fields -from django.db import migrations -import djmoney.models.fields -import djmoney.models.validators - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0118_auto_20231024_1844'), - ] - - operations = [ - migrations.AddField( - model_name='partpricing', - name='override_max', - field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Override maximum cost', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Maximum Cost'), - ), - migrations.AddField( - model_name='partpricing', - name='override_max_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - migrations.AddField( - model_name='partpricing', - name='override_min', - field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Override minimum cost', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Minimum Cost'), - ), - migrations.AddField( - model_name='partpricing', - name='override_min_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0120_parttesttemplate_key.py b/src/backend/InvenTree/part/migrations/0120_parttesttemplate_key.py deleted file mode 100644 index 82954ac785..0000000000 --- a/src/backend/InvenTree/part/migrations/0120_parttesttemplate_key.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.2.9 on 2024-02-07 03:43 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0119_auto_20231120_0457'), - ] - - operations = [ - migrations.AddField( - model_name='parttesttemplate', - name='key', - field=models.CharField(blank=True, help_text='Simplified key for the test', max_length=100, verbose_name='Test Key'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0121_auto_20240207_0344.py b/src/backend/InvenTree/part/migrations/0121_auto_20240207_0344.py deleted file mode 100644 index 6e028a0645..0000000000 --- a/src/backend/InvenTree/part/migrations/0121_auto_20240207_0344.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 4.2.9 on 2024-02-07 03:44 - -from django.db import migrations - - -def set_key(apps, schema_editor): - """Create a 'key' value for existing PartTestTemplate objects.""" - - import InvenTree.helpers - - PartTestTemplate = apps.get_model('part', 'PartTestTemplate') - - for template in PartTestTemplate.objects.all(): - template.key = InvenTree.helpers.generateTestKey(str(template.test_name).strip()) - template.save() - - if PartTestTemplate.objects.count() > 0: - print(f"\nUpdated 'key' value for {PartTestTemplate.objects.count()} PartTestTemplate objects") - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0120_parttesttemplate_key'), - ] - - operations = [ - migrations.RunPython(set_key, reverse_code=migrations.RunPython.noop) - ] diff --git a/src/backend/InvenTree/part/migrations/0122_parttesttemplate_enabled.py b/src/backend/InvenTree/part/migrations/0122_parttesttemplate_enabled.py deleted file mode 100644 index f7d1fece00..0000000000 --- a/src/backend/InvenTree/part/migrations/0122_parttesttemplate_enabled.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.2.10 on 2024-02-20 01:35 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0121_auto_20240207_0344'), - ] - - operations = [ - migrations.AddField( - model_name='parttesttemplate', - name='enabled', - field=models.BooleanField(default=True, help_text='Is this test enabled?', verbose_name='Enabled'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0123_parttesttemplate_choices.py b/src/backend/InvenTree/part/migrations/0123_parttesttemplate_choices.py deleted file mode 100644 index df26051812..0000000000 --- a/src/backend/InvenTree/part/migrations/0123_parttesttemplate_choices.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.2.12 on 2024-06-05 01:14 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0122_parttesttemplate_enabled'), - ] - - operations = [ - migrations.AddField( - model_name='parttesttemplate', - name='choices', - field=models.CharField(blank=True, help_text='Valid choices for this test (comma-separated)', max_length=5000, verbose_name='Choices'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0124_delete_partattachment.py b/src/backend/InvenTree/part/migrations/0124_delete_partattachment.py deleted file mode 100644 index 5213211aa4..0000000000 --- a/src/backend/InvenTree/part/migrations/0124_delete_partattachment.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 4.2.12 on 2024-06-09 09:02 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0050_auto_20240508_0138'), - ('common', '0026_auto_20240608_1238'), - ('company', '0069_company_active'), - ('order', '0099_alter_salesorder_status'), - ('part', '0123_parttesttemplate_choices'), - ('stock', '0110_alter_stockitemtestresult_finished_datetime_and_more') - ] - - operations = [ - migrations.DeleteModel( - name='PartAttachment', - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0125_part_locked.py b/src/backend/InvenTree/part/migrations/0125_part_locked.py deleted file mode 100644 index a5ac79876c..0000000000 --- a/src/backend/InvenTree/part/migrations/0125_part_locked.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.2.12 on 2024-06-27 01:39 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0124_delete_partattachment'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='locked', - field=models.BooleanField(default=False, help_text='Locked parts cannot be edited', verbose_name='Locked'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0126_part_revision_of.py b/src/backend/InvenTree/part/migrations/0126_part_revision_of.py deleted file mode 100644 index e5324d60a9..0000000000 --- a/src/backend/InvenTree/part/migrations/0126_part_revision_of.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 4.2.12 on 2024-07-07 04:42 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0125_part_locked'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='revision_of', - field=models.ForeignKey(help_text='Is this part a revision of another part?', null=True, blank=True, on_delete=django.db.models.deletion.SET_NULL, related_name='revisions', to='part.part', verbose_name='Revision Of'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0127_remove_partcategory_icon_partcategory__icon.py b/src/backend/InvenTree/part/migrations/0127_remove_partcategory_icon_partcategory__icon.py deleted file mode 100644 index 0e9e148fb2..0000000000 --- a/src/backend/InvenTree/part/migrations/0127_remove_partcategory_icon_partcategory__icon.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 4.2.11 on 2024-07-20 22:30 - -import common.icons -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0126_part_revision_of'), - ] - - operations = [ - migrations.SeparateDatabaseAndState( - database_operations=[], - state_operations=[ - migrations.RenameField( - model_name='partcategory', - old_name='icon', - new_name='_icon', - ), - migrations.AlterField( - model_name='partcategory', - name='_icon', - field=models.CharField(blank=True, db_column='icon', help_text='Icon (optional)', max_length=100, validators=[common.icons.validate_icon], verbose_name='Icon'), - ), - ], - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0128_part_testable.py b/src/backend/InvenTree/part/migrations/0128_part_testable.py deleted file mode 100644 index 094299a8de..0000000000 --- a/src/backend/InvenTree/part/migrations/0128_part_testable.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.2.15 on 2024-08-15 02:12 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0127_remove_partcategory_icon_partcategory__icon'), - ] - - operations = [ - migrations.AddField( - model_name='part', - name='testable', - field=models.BooleanField(default=False, help_text='Can this part have test results recorded against it?', verbose_name='Testable'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0129_auto_20240815_0214.py b/src/backend/InvenTree/part/migrations/0129_auto_20240815_0214.py deleted file mode 100644 index 0f406286a3..0000000000 --- a/src/backend/InvenTree/part/migrations/0129_auto_20240815_0214.py +++ /dev/null @@ -1,37 +0,0 @@ -# Generated by Django 4.2.15 on 2024-08-15 02:14 - -from django.db import migrations - - -def set_testable(apps, schema_editor): - """Set the 'testable' status to True for certain parts. - - Prior to migration part.0128, the 'trackable' attribute - was used to determine if parts could have tests associated with them. - - However, 'trackable' comes with other restrictions - (such as requiring a unique serial number). - - So, we have added a new field 'testable' to the Part model, - which is updated in this migration to match the value of the 'trackable' field. - """ - - Part = apps.get_model('part', 'Part') - - # By default, 'testable' is False - so we only need to update parts marked as 'trackable' - trackable_parts = Part.objects.filter(trackable=True) - - if trackable_parts.count() > 0: - print(f"\nMarking {trackable_parts.count()} Part objects as 'testable'") - trackable_parts.update(testable=True) - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0128_part_testable'), - ] - - operations = [ - migrations.RunPython(set_testable, reverse_code=migrations.RunPython.noop) - ] diff --git a/src/backend/InvenTree/part/migrations/0130_alter_parttesttemplate_part.py b/src/backend/InvenTree/part/migrations/0130_alter_parttesttemplate_part.py deleted file mode 100644 index 449baff6fe..0000000000 --- a/src/backend/InvenTree/part/migrations/0130_alter_parttesttemplate_part.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 4.2.15 on 2024-08-29 03:24 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0129_auto_20240815_0214'), - ] - - operations = [ - migrations.AlterField( - model_name='parttesttemplate', - name='part', - field=models.ForeignKey(limit_choices_to={'testable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='test_templates', to='part.part', verbose_name='Part'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0131_partrelated_note.py b/src/backend/InvenTree/part/migrations/0131_partrelated_note.py deleted file mode 100644 index 24d08cbcfb..0000000000 --- a/src/backend/InvenTree/part/migrations/0131_partrelated_note.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.2.16 on 2024-11-19 12:50 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0130_alter_parttesttemplate_part'), - ] - - operations = [ - migrations.AddField( - model_name='partrelated', - name='note', - field=models.CharField(blank=True, help_text='Note for this relationship', max_length=500, verbose_name='Note'), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0132_partparametertemplate_selectionlist.py b/src/backend/InvenTree/part/migrations/0132_partparametertemplate_selectionlist.py deleted file mode 100644 index e30ffb34c9..0000000000 --- a/src/backend/InvenTree/part/migrations/0132_partparametertemplate_selectionlist.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 4.2.16 on 2024-11-24 12:41 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ('common', '0032_selectionlist_selectionlistentry_and_more'), - ('part', '0131_partrelated_note'), - ] - - operations = [ - migrations.AddField( - model_name='partparametertemplate', - name='selectionlist', - field=models.ForeignKey( - blank=True, - help_text='Selection list for this parameter', - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name='parameter_templates', - to='common.selectionlist', - verbose_name='Selection List', - ), - ) - ] diff --git a/src/backend/InvenTree/part/migrations/0133_alter_partcategory__icon.py b/src/backend/InvenTree/part/migrations/0133_alter_partcategory__icon.py deleted file mode 100644 index 754d08cad6..0000000000 --- a/src/backend/InvenTree/part/migrations/0133_alter_partcategory__icon.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 4.2.19 on 2025-02-25 22:14 - -import common.icons -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("part", "0132_partparametertemplate_selectionlist"), - ] - - operations = [ - migrations.AlterField( - model_name="partcategory", - name="_icon", - field=models.CharField( - blank=True, - db_column="icon", - help_text="Icon (optional)", - max_length=100, - null=True, - validators=[common.icons.validate_icon], - verbose_name="Icon", - ), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0134_auto_20250221_1309.py b/src/backend/InvenTree/part/migrations/0134_auto_20250221_1309.py deleted file mode 100644 index f46c453ffa..0000000000 --- a/src/backend/InvenTree/part/migrations/0134_auto_20250221_1309.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 4.2.19 on 2025-02-21 13:09 - -import InvenTree.fields -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("part", "0133_alter_partcategory__icon"), - ] - - operations = [ - migrations.AlterField( - model_name='part', - name='link', - field=models.TextField(null=True, blank=True) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0135_alter_part_link.py b/src/backend/InvenTree/part/migrations/0135_alter_part_link.py deleted file mode 100644 index 76a0ebb4a7..0000000000 --- a/src/backend/InvenTree/part/migrations/0135_alter_part_link.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 4.2.19 on 2025-02-21 13:46 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("part", "0134_auto_20250221_1309"), - ] - - operations = [ - migrations.AlterField( - model_name="part", - name="link", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="Link to external URL", - max_length=2000, - null=True, - verbose_name="Link", - ), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0136_partparameter_note_partparameter_updated_and_more.py b/src/backend/InvenTree/part/migrations/0136_partparameter_note_partparameter_updated_and_more.py deleted file mode 100644 index 3867769f6f..0000000000 --- a/src/backend/InvenTree/part/migrations/0136_partparameter_note_partparameter_updated_and_more.py +++ /dev/null @@ -1,50 +0,0 @@ -# Generated by Django 4.2.23 on 2025-07-15 02:05 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ("part", "0135_alter_part_link"), - ] - - operations = [ - migrations.AddField( - model_name="partparameter", - name="note", - field=models.CharField( - blank=True, - help_text="Optional note field", - max_length=500, - verbose_name="Note", - ), - ), - migrations.AddField( - model_name="partparameter", - name="updated", - field=models.DateTimeField( - blank=True, - default=None, - help_text="Timestamp of last update", - null=True, - verbose_name="Updated", - ), - ), - migrations.AddField( - model_name="partparameter", - name="updated_by", - field=models.ForeignKey( - blank=True, - help_text="User who last updated this object", - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="%(class)s_updated", - to=settings.AUTH_USER_MODEL, - verbose_name="Update By", - ), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0137_bomitem_attrition_bomitem_rounding_multiple_and_more.py b/src/backend/InvenTree/part/migrations/0137_bomitem_attrition_bomitem_rounding_multiple_and_more.py deleted file mode 100644 index 971b20ff89..0000000000 --- a/src/backend/InvenTree/part/migrations/0137_bomitem_attrition_bomitem_rounding_multiple_and_more.py +++ /dev/null @@ -1,55 +0,0 @@ -# Generated by Django 4.2.23 on 2025-07-18 23:40 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("part", "0136_partparameter_note_partparameter_updated_and_more"), - ] - - operations = [ - migrations.AddField( - model_name="bomitem", - name="attrition", - field=models.DecimalField( - decimal_places=3, - default=0, - help_text="Estimated attrition for a build, expressed as a percentage (0-100)", - max_digits=6, - validators=[ - django.core.validators.MinValueValidator(0), - django.core.validators.MaxValueValidator(100), - ], - verbose_name="Attrition", - ), - ), - migrations.AddField( - model_name="bomitem", - name="rounding_multiple", - field=models.DecimalField( - blank=True, - decimal_places=5, - default=None, - help_text="Round up required production quantity to nearest multiple of this value", - max_digits=15, - null=True, - validators=[django.core.validators.MinValueValidator(0)], - verbose_name="Rounding Multiple", - ), - ), - migrations.AddField( - model_name="bomitem", - name="setup_quantity", - field=models.DecimalField( - decimal_places=5, - default=0, - help_text="Extra required quantity for a build, to account for setup losses", - max_digits=15, - validators=[django.core.validators.MinValueValidator(0)], - verbose_name="Setup Quantity", - ), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0138_auto_20250718_2342.py b/src/backend/InvenTree/part/migrations/0138_auto_20250718_2342.py deleted file mode 100644 index 84f0a41f25..0000000000 --- a/src/backend/InvenTree/part/migrations/0138_auto_20250718_2342.py +++ /dev/null @@ -1,88 +0,0 @@ -# Generated by Django 4.2.23 on 2025-07-18 23:42 - -from decimal import Decimal - -from django.db import migrations - - -def convert_overage(apps, schema_editor): - """Convert 'overage' field to 'setup_quantity' and 'attrition' fields. - - - The 'overage' field is split into two new fields: - - 'setup_quantity': The integer part of the overage. - - 'attrition': The fractional part of the overage. - """ - - BomItem = apps.get_model('part', 'BomItem') - - # Fetch all BomItem objects with a non-zero overage - bom_items = BomItem.objects.exclude(overage='').exclude(overage=None).distinct() - - if bom_items.count() == 0: - return - - print(f"\nConverting {bom_items.count()} BomItem objects with 'overage' to 'setup_quantity' and 'attrition'") - - for item in bom_items: - - # First attempt - convert to a percentage - overage = str(item.overage).strip() - - if overage.endswith('%'): - try: - attrition = Decimal(overage[:-1]) - attrition = max(0, attrition) # Ensure it's not negative - attrition = min(100, attrition) # Cap at 100% - - item.attrition = attrition - item.setup_quantity = Decimal(0) - item.save() - except Exception as e: - print(f" - Error converting {item.pk} from percentage: {e}") - continue - - else: - # If not a percentage, treat it as a decimal number - try: - setup_quantity = Decimal(overage) - setup_quantity = max(0, setup_quantity) # Ensure it's not negative - - item.setup_quantity = setup_quantity - item.attrition = Decimal(0) - item.save() - except Exception as e: - print(f"- Error converting {item.pk} from decimal: {e}") - continue - - -def revert_overage(apps, schema_editor): - """Revert the 'setup_quantity' and 'attrition' fields back to 'overage'. - - - Combines 'setup_quantity' and 'attrition' back into the 'overage' field. - """ - - BomItem = apps.get_model('part', 'BomItem') - - # First, convert all 'attrition' values to percentages - for item in BomItem.objects.exclude(attrition=0).distinct(): - item.overage = f"{item.attrition}%" - item.save() - - # Second, convert all 'setup_quantity' values to strings - for item in BomItem.objects.exclude(setup_quantity=0).distinct(): - item.overage = str(item.setup_quantity or 0) - item.save() - - -class Migration(migrations.Migration): - - dependencies = [ - ("part", "0137_bomitem_attrition_bomitem_rounding_multiple_and_more"), - ] - - operations = [ - migrations.RunPython( - code=convert_overage, - reverse_code=revert_overage, - ) - ] diff --git a/src/backend/InvenTree/part/migrations/0139_remove_bomitem_overage.py b/src/backend/InvenTree/part/migrations/0139_remove_bomitem_overage.py deleted file mode 100644 index d41e155a3b..0000000000 --- a/src/backend/InvenTree/part/migrations/0139_remove_bomitem_overage.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 4.2.23 on 2025-07-19 00:22 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("part", "0138_auto_20250718_2342"), - ] - - operations = [ - migrations.RemoveField( - model_name="bomitem", - name="overage", - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0140_part_bom_validated.py b/src/backend/InvenTree/part/migrations/0140_part_bom_validated.py deleted file mode 100644 index 7accbafacc..0000000000 --- a/src/backend/InvenTree/part/migrations/0140_part_bom_validated.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 4.2.23 on 2025-07-22 01:57 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("part", "0139_remove_bomitem_overage"), - ] - - operations = [ - migrations.AddField( - model_name="part", - name="bom_validated", - field=models.BooleanField( - default=False, - help_text="Is the BOM for this part valid?", - verbose_name="BOM Validated", - ), - ), - ] diff --git a/src/backend/InvenTree/part/migrations/0141_auto_20250722_0303.py b/src/backend/InvenTree/part/migrations/0141_auto_20250722_0303.py deleted file mode 100644 index 1b0496dd27..0000000000 --- a/src/backend/InvenTree/part/migrations/0141_auto_20250722_0303.py +++ /dev/null @@ -1,80 +0,0 @@ -# Generated by Django 4.2.23 on 2025-07-22 03:03 - -from django.db import migrations - -def cache_bom_valid(apps, schema_editor): - """Calculate and cache the BOM validity for all parts. - - Procedure: - - - Find all parts which have linked BOM item(s) - - Limit to parts which have a stored BOM checksum - - For each such part, calculate and update the BOM "validity" - """ - - from InvenTree.tasks import offload_task - from part.tasks import check_bom_valid - - Part = apps.get_model('part', 'Part') - BomItem = apps.get_model('part', 'BomItem') - - # Fetch all BomItem objects - bom_items = BomItem.objects.exclude(part=None).prefetch_related('part').distinct() - - parts_to_update = set() - - for item in bom_items: - - # Parts associated with this BomItem - parts = [] - - if item.inherited: - # Find all inherited assemblies for this BomItem - parts = list( - Part.objects.filter( - tree_id=item.part.tree_id, - lft__gte=item.part.lft, - rght__lte=item.part.rght - ) - ) - else: - parts = [item.part] - - for part in parts: - # Part has already been observed - skip - if part in parts_to_update: - continue - - # Part has no BOM checksum - skip - if not part.bom_checksum: - continue - - # Part has not already been validated - if not part.bom_checked_date: - continue - - parts_to_update.add(part) - - if len(parts_to_update) > 0: - print(f"\nScheduling {len(parts_to_update)} parts to update BOM validity.") - - for part in parts_to_update: - # Offload task to recalculate the BOM checksum for this part - # The background worker will process these when the server restarts - offload_task( - check_bom_valid, - part.pk, - force_async=True, - group='part' - ) - - -class Migration(migrations.Migration): - - dependencies = [ - ("part", "0140_part_bom_validated"), - ] - - operations = [ - migrations.RunPython(cache_bom_valid, migrations.RunPython.noop), - ] diff --git a/src/backend/InvenTree/part/migrations/0142_remove_part_last_stocktake_remove_partstocktake_note_and_more.py b/src/backend/InvenTree/part/migrations/0142_remove_part_last_stocktake_remove_partstocktake_note_and_more.py deleted file mode 100644 index 98a6b14be1..0000000000 --- a/src/backend/InvenTree/part/migrations/0142_remove_part_last_stocktake_remove_partstocktake_note_and_more.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 4.2.23 on 2025-08-04 08:32 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("part", "0141_auto_20250722_0303"), - ] - - operations = [ - migrations.RemoveField( - model_name="part", - name="last_stocktake", - ), - migrations.RemoveField( - model_name="partstocktake", - name="note", - ), - migrations.RemoveField( - model_name="partstocktake", - name="user", - ), - migrations.DeleteModel( - name="PartStocktakeReport", - ), - ] diff --git a/src/backend/InvenTree/part/test_migrations.py b/src/backend/InvenTree/part/test_migrations.py index 643ec7043f..8313bb6f24 100644 --- a/src/backend/InvenTree/part/test_migrations.py +++ b/src/backend/InvenTree/part/test_migrations.py @@ -48,233 +48,6 @@ class TestForwardMigrations(MigratorTestCase): self.assertEqual(part.description, f'My part {name}') -class TestBomItemMigrations(MigratorTestCase): - """Tests for BomItem migrations.""" - - migrate_from = ('part', '0002_auto_20190520_2204') - migrate_to = ('part', '0101_bomitem_validated') - - def prepare(self): - """Create initial dataset.""" - Part = self.old_state.apps.get_model('part', 'part') - BomItem = self.old_state.apps.get_model('part', 'bomitem') - - a = Part.objects.create(name='Part A', description='My part A') - b = Part.objects.create(name='Part B', description='My part B') - c = Part.objects.create(name='Part C', description='My part C') - - BomItem.objects.create(part=a, sub_part=b, quantity=1) - BomItem.objects.create(part=a, sub_part=c, quantity=1) - - self.assertEqual(BomItem.objects.count(), 2) - - # Initially we don't have the 'validated' field - with self.assertRaises(AttributeError): - print(b.validated) - - def test_validated_field(self): - """Test that the 'validated' field is added to the BomItem objects.""" - BomItem = self.new_state.apps.get_model('part', 'bomitem') - - self.assertEqual(BomItem.objects.count(), 2) - - for bom_item in BomItem.objects.all(): - self.assertFalse(bom_item.validated) - - -class TestParameterMigrations(MigratorTestCase): - """Unit test for part parameter migrations.""" - - migrate_from = ('part', '0106_part_tags') - migrate_to = ('part', '0143_alter_part_image') - - def prepare(self): - """Create some parts, and templates with parameters.""" - Part = self.old_state.apps.get_model('part', 'part') - PartParameter = self.old_state.apps.get_model('part', 'partparameter') - PartParameterTemlate = self.old_state.apps.get_model( - 'part', 'partparametertemplate' - ) - - # Create some parts - a = Part.objects.create( - name='Part A', description='My part A', level=0, lft=0, rght=0, tree_id=0 - ) - - b = Part.objects.create( - name='Part B', description='My part B', level=0, lft=0, rght=0, tree_id=0 - ) - - # Create some templates - t1 = PartParameterTemlate.objects.create(name='Template 1', units='mm') - t2 = PartParameterTemlate.objects.create(name='Template 2', units='AMPERE') - - # Create some parameter values - PartParameter.objects.create(part=a, template=t1, data='1.0') - PartParameter.objects.create(part=a, template=t2, data='-2mA') - - PartParameter.objects.create(part=b, template=t1, data='1/10 inch') - PartParameter.objects.create(part=b, template=t2, data='abc') - - def test_data_migration(self): - """Test that the template units and values have been updated correctly.""" - Part = self.new_state.apps.get_model('part', 'part') - PartParameter = self.new_state.apps.get_model('part', 'partparameter') - PartParameterTemlate = self.new_state.apps.get_model( - 'part', 'partparametertemplate' - ) - - # Extract the parts - a = Part.objects.get(name='Part A') - b = Part.objects.get(name='Part B') - - # Check that the templates have been updated correctly - t1 = PartParameterTemlate.objects.get(name='Template 1') - self.assertEqual(t1.units, 'mm') - - t2 = PartParameterTemlate.objects.get(name='Template 2') - self.assertEqual(t2.units, 'ampere') - - # Check that the parameter values have been updated correctly - p1 = PartParameter.objects.get(part=a, template=t1) - self.assertEqual(p1.data, '1.0') - self.assertEqual(p1.data_numeric, 1.0) - - p2 = PartParameter.objects.get(part=a, template=t2) - self.assertEqual(p2.data, '-2mA') - self.assertEqual(p2.data_numeric, -0.002) - - p3 = PartParameter.objects.get(part=b, template=t1) - self.assertEqual(p3.data, '1/10 inch') - self.assertEqual(p3.data_numeric, 2.54) - - # This one has not converted correctly - p4 = PartParameter.objects.get(part=b, template=t2) - self.assertEqual(p4.data, 'abc') - self.assertEqual(p4.data_numeric, None) - - -class PartUnitsMigrationTest(MigratorTestCase): - """Test for data migration of Part.units field.""" - - migrate_from = ('part', '0109_auto_20230517_1048') - migrate_to = ('part', '0115_part_responsible_owner') - - def prepare(self): - """Prepare some parts with units.""" - Part = self.old_state.apps.get_model('part', 'part') - - units = ['mm', 'INCH', '', '%'] - - for idx, unit in enumerate(units): - Part.objects.create( - name=f'Part {idx + 1}', - description=f'My part at index {idx}', - units=unit, - level=0, - lft=0, - rght=0, - tree_id=0, - ) - - def test_units_migration(self): - """Test that the units have migrated OK.""" - Part = self.new_state.apps.get_model('part', 'part') - - part_1 = Part.objects.get(name='Part 1') - part_2 = Part.objects.get(name='Part 2') - part_3 = Part.objects.get(name='Part 3') - part_4 = Part.objects.get(name='Part 4') - - self.assertEqual(part_1.units, 'mm') - self.assertEqual(part_2.units, 'inch') - self.assertEqual(part_3.units, '') - self.assertEqual(part_4.units, 'percent') - - -class TestPartParameterTemplateMigration(MigratorTestCase): - """Test for data migration of PartParameterTemplate. - - Ref: https://github.com/inventree/InvenTree/pull/4987 - """ - - migrate_from = ('part', '0110_alter_part_units') - migrate_to = ('part', '0113_auto_20230531_1205') - - def prepare(self): - """Prepare some parts with units.""" - PartParameterTemplate = self.old_state.apps.get_model( - 'part', 'partparametertemplate' - ) - - # Create a test template - template = PartParameterTemplate.objects.create( - name='Template 1', description='a part parameter template' - ) - - # Ensure that the 'choices' and 'checkbox' fields do not exist - with self.assertRaises(AttributeError): - template.choices - - with self.assertRaises(AttributeError): - template.checkbox - - def test_units_migration(self): - """Test that the new fields have been added correctly.""" - PartParameterTemplate = self.new_state.apps.get_model( - 'part', 'partparametertemplate' - ) - - template = PartParameterTemplate.objects.get(name='Template 1') - - self.assertEqual(template.choices, '') - self.assertEqual(template.checkbox, False) - - -class TestPartTestParameterMigration(MigratorTestCase): - """Unit tests for the PartTestTemplate model migrations.""" - - migrate_from = ('part', '0119_auto_20231120_0457') - migrate_to = ('part', '0121_auto_20240207_0344') - - test_keys = { - 'atest': 'A test', - 'someresult': 'Some result', - 'anotherresult': 'Another result', - } - - def prepare(self): - """Setup initial database state.""" - Part = self.old_state.apps.get_model('part', 'part') - PartTestTemplate = self.old_state.apps.get_model('part', 'parttesttemplate') - - # Create a part - p = Part.objects.create( - name='Test Part', - description='A test part', - level=0, - lft=0, - rght=0, - tree_id=0, - ) - - # Create some test templates - for v in self.test_keys.values(): - PartTestTemplate.objects.create( - test_name=v, part=p, description='A test template' - ) - - self.assertEqual(PartTestTemplate.objects.count(), 3) - - def test_key_field(self): - """Self that the key field is created and correctly filled.""" - PartTestTemplate = self.new_state.apps.get_model('part', 'parttesttemplate') - - for key, value in self.test_keys.items(): - template = PartTestTemplate.objects.get(test_name=value) - self.assertEqual(template.key, key) - - class TestPartParameterDeletion(MigratorTestCase): """Test for PartParameter deletion migration. diff --git a/src/backend/InvenTree/stock/migrations/0002_auto_20190525_2226.py b/src/backend/InvenTree/stock/migrations/0002_auto_20190525_2226.py deleted file mode 100644 index b8607c3ec7..0000000000 --- a/src/backend/InvenTree/stock/migrations/0002_auto_20190525_2226.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-25 12:26 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='part', - field=models.ForeignKey(help_text='Base part', limit_choices_to={'active': True, 'has_variants': True}, on_delete=django.db.models.deletion.CASCADE, related_name='stock_items', to='part.Part'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0002_auto_20190525_2226_squashed_0030_auto_20200422_0015.py b/src/backend/InvenTree/stock/migrations/0002_auto_20190525_2226_squashed_0030_auto_20200422_0015.py new file mode 100644 index 0000000000..7b13ab9043 --- /dev/null +++ b/src/backend/InvenTree/stock/migrations/0002_auto_20190525_2226_squashed_0030_auto_20200422_0015.py @@ -0,0 +1,272 @@ +# Generated by Django 5.2.17 on 2026-09-09 06:48 + +import InvenTree.fields +import InvenTree.validators +import django.core.validators +import django.db.models.deletion +import mptt.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + replaces = [ + ("stock", "0002_auto_20190525_2226"), + ("stock", "0003_auto_20190525_2303"), + ("stock", "0004_auto_20190525_2356"), + ("stock", "0005_auto_20190602_1944"), + ("stock", "0006_stockitem_purchase_order"), + ("stock", "0007_auto_20190618_0042"), + ("stock", "0008_stockitemtracking_url"), + ("stock", "0009_auto_20190715_2351"), + ("stock", "0010_stockitem_build"), + ("stock", "0011_auto_20190908_0404"), + ("stock", "0012_auto_20190908_0405"), + ("stock", "0013_auto_20190908_0916"), + ("stock", "0014_auto_20190908_0918"), + ("stock", "0015_auto_20190913_1407"), + ("stock", "0016_auto_20191118_2146"), + ("stock", "0017_auto_20191118_2311"), + ("stock", "0018_auto_20200202_0103"), + ("stock", "0019_auto_20200202_1024"), + ("stock", "0020_auto_20200206_1213"), + ("stock", "0021_auto_20200215_2232"), + ("stock", "0022_auto_20200217_1109"), + ("stock", "0023_auto_20200318_1027"), + ("stock", "0024_auto_20200405_2239"), + ("stock", "0025_auto_20200405_2243"), + ("stock", "0026_stockitem_uid"), + ("stock", "0027_stockitem_sales_order"), + ("stock", "0028_auto_20200421_0724"), + ("stock", "0029_auto_20200421_2359"), + ("stock", "0030_auto_20200422_0015"), + ] + + dependencies = [ + ("build", "0005_auto_20190604_2217"), + ("order", "0010_purchaseorderlineitem_notes"), + ("order", "0023_auto_20200420_2309"), + ("stock", "0001_initial"), + ] + + operations = [ + migrations.AlterField( + model_name="stockitem", + name="part", + field=models.ForeignKey( + help_text="Base part", + limit_choices_to={ + "active": True, + "is_template": False, + "virtual": False, + }, + on_delete=django.db.models.deletion.CASCADE, + related_name="stock_items", + to="part.part", + ), + ), + migrations.AlterField( + model_name="stockitem", + name="status", + field=models.PositiveIntegerField( + choices=[ + (10, "OK"), + (50, "Attention needed"), + (55, "Damaged"), + (60, "Destroyed"), + (70, "Lost"), + (110, "Shipped"), + (85, "Returned"), + ], + default=10, + validators=[django.core.validators.MinValueValidator(0)], + ), + ), + migrations.AddField( + model_name="stockitem", + name="purchase_order", + field=models.ForeignKey( + blank=True, + help_text="Purchase order for this stock item", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="stock_items", + to="order.purchaseorder", + ), + ), + migrations.AlterField( + model_name="stocklocation", + name="name", + field=models.CharField( + max_length=100, validators=[InvenTree.validators.validate_tree_name] + ), + ), + migrations.AlterField( + model_name="stockitemtracking", + name="notes", + field=models.CharField(blank=True, help_text="Entry notes", max_length=512), + ), + migrations.AlterField( + model_name="stockitemtracking", + name="title", + field=models.CharField(help_text="Tracking entry title", max_length=250), + ), + migrations.AddField( + model_name="stockitem", + name="build", + field=models.ForeignKey( + blank=True, + help_text="Build for this stock item", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="build_outputs", + to="build.build", + ), + ), + migrations.AddField( + model_name="stocklocation", + name="level", + field=models.PositiveIntegerField(default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name="stocklocation", + name="lft", + field=models.PositiveIntegerField(default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name="stocklocation", + name="rght", + field=models.PositiveIntegerField(default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name="stocklocation", + name="tree_id", + field=models.PositiveIntegerField(db_index=True, default=0, editable=False), + preserve_default=False, + ), + migrations.AlterField( + model_name="stockitem", + name="location", + field=mptt.fields.TreeForeignKey( + blank=True, + help_text="Where is this stock item located?", + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="stock_items", + to="stock.stocklocation", + ), + ), + migrations.AlterField( + model_name="stocklocation", + name="parent", + field=mptt.fields.TreeForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="children", + to="stock.stocklocation", + ), + ), + migrations.RenameField( + model_name="stockitem", + old_name="URL", + new_name="link", + ), + migrations.AlterField( + model_name="stockitem", + name="link", + field=InvenTree.fields.InvenTreeURLField( + blank=True, help_text="Link to external URL", max_length=2000 + ), + ), + migrations.AlterField( + model_name="stockitem", + name="quantity", + field=models.DecimalField( + decimal_places=5, + default=1, + max_digits=15, + validators=[django.core.validators.MinValueValidator(0)], + ), + ), + migrations.AlterField( + model_name="stockitemtracking", + name="quantity", + field=models.DecimalField( + decimal_places=5, + default=1, + max_digits=15, + validators=[django.core.validators.MinValueValidator(0)], + ), + ), + migrations.AlterField( + model_name="stockitem", + name="notes", + field=models.TextField(blank=True, help_text="Stock Item Notes", null=True), + ), + migrations.AddField( + model_name="stockitem", + name="level", + field=models.PositiveIntegerField(default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name="stockitem", + name="lft", + field=models.PositiveIntegerField(default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name="stockitem", + name="parent", + field=mptt.fields.TreeForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="children", + to="stock.stockitem", + ), + ), + migrations.AddField( + model_name="stockitem", + name="rght", + field=models.PositiveIntegerField(default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name="stockitem", + name="tree_id", + field=models.PositiveIntegerField(db_index=True, default=0, editable=False), + preserve_default=False, + ), + migrations.AddField( + model_name="stockitemtracking", + name="link", + field=InvenTree.fields.InvenTreeURLField( + blank=True, + help_text="Link to external page for further information", + max_length=2000, + ), + ), + migrations.AddField( + model_name="stockitem", + name="uid", + field=models.CharField( + blank=True, help_text="Unique identifier field", max_length=128 + ), + ), + migrations.AddField( + model_name="stockitem", + name="sales_order_line", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="stock_items", + to="order.salesorderlineitem", + ), + ), + ] diff --git a/src/backend/InvenTree/stock/migrations/0003_auto_20190525_2303.py b/src/backend/InvenTree/stock/migrations/0003_auto_20190525_2303.py deleted file mode 100644 index e8f7708cfe..0000000000 --- a/src/backend/InvenTree/stock/migrations/0003_auto_20190525_2303.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-25 13:03 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0002_auto_20190525_2226'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='part', - field=models.ForeignKey(help_text='Base part', limit_choices_to={'active': True, 'has_variants': False}, on_delete=django.db.models.deletion.CASCADE, related_name='stock_items', to='part.Part'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0004_auto_20190525_2356.py b/src/backend/InvenTree/stock/migrations/0004_auto_20190525_2356.py deleted file mode 100644 index 4b2f939eb2..0000000000 --- a/src/backend/InvenTree/stock/migrations/0004_auto_20190525_2356.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-05-25 13:56 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0003_auto_20190525_2303'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='part', - field=models.ForeignKey(help_text='Base part', limit_choices_to={'active': True, 'is_template': False}, on_delete=django.db.models.deletion.CASCADE, related_name='stock_items', to='part.Part'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0005_auto_20190602_1944.py b/src/backend/InvenTree/stock/migrations/0005_auto_20190602_1944.py deleted file mode 100644 index 1d134288cc..0000000000 --- a/src/backend/InvenTree/stock/migrations/0005_auto_20190602_1944.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2 on 2019-06-02 09:44 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0004_auto_20190525_2356'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='status', - field=models.PositiveIntegerField(choices=[(10, 'OK'), (50, 'Attention needed'), (55, 'Damaged'), (60, 'Destroyed'), (70, 'Lost')], default=10, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0006_stockitem_purchase_order.py b/src/backend/InvenTree/stock/migrations/0006_stockitem_purchase_order.py deleted file mode 100644 index 08d7f4de01..0000000000 --- a/src/backend/InvenTree/stock/migrations/0006_stockitem_purchase_order.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2.2 on 2019-06-15 09:06 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0010_purchaseorderlineitem_notes'), - ('stock', '0005_auto_20190602_1944'), - ] - - operations = [ - migrations.AddField( - model_name='stockitem', - name='purchase_order', - field=models.ForeignKey(blank=True, help_text='Purchase order for this stock item', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stock_items', to='order.PurchaseOrder'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0007_auto_20190618_0042.py b/src/backend/InvenTree/stock/migrations/0007_auto_20190618_0042.py deleted file mode 100644 index 862ea94273..0000000000 --- a/src/backend/InvenTree/stock/migrations/0007_auto_20190618_0042.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.2 on 2019-06-17 14:42 - -import InvenTree.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0006_stockitem_purchase_order'), - ] - - operations = [ - migrations.AlterField( - model_name='stocklocation', - name='name', - field=models.CharField(max_length=100, unique=True, validators=[InvenTree.validators.validate_tree_name]), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0008_stockitemtracking_url.py b/src/backend/InvenTree/stock/migrations/0008_stockitemtracking_url.py deleted file mode 100644 index e27f7f8854..0000000000 --- a/src/backend/InvenTree/stock/migrations/0008_stockitemtracking_url.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.2 on 2019-07-15 11:10 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0007_auto_20190618_0042'), - ] - - operations = [ - migrations.AddField( - model_name='stockitemtracking', - name='URL', - field=models.URLField(blank=True, help_text='Link to external page for further information'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0009_auto_20190715_2351.py b/src/backend/InvenTree/stock/migrations/0009_auto_20190715_2351.py deleted file mode 100644 index 230ca68401..0000000000 --- a/src/backend/InvenTree/stock/migrations/0009_auto_20190715_2351.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 2.2.2 on 2019-07-15 13:51 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0008_stockitemtracking_url'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitemtracking', - name='notes', - field=models.CharField(blank=True, help_text='Entry notes', max_length=512), - ), - migrations.AlterField( - model_name='stockitemtracking', - name='title', - field=models.CharField(help_text='Tracking entry title', max_length=250), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0010_stockitem_build.py b/src/backend/InvenTree/stock/migrations/0010_stockitem_build.py deleted file mode 100644 index 4445d27b2e..0000000000 --- a/src/backend/InvenTree/stock/migrations/0010_stockitem_build.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2.4 on 2019-09-01 13:08 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0005_auto_20190604_2217'), - ('stock', '0009_auto_20190715_2351'), - ] - - operations = [ - migrations.AddField( - model_name='stockitem', - name='build', - field=models.ForeignKey(blank=True, help_text='Build for this stock item', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='build_outputs', to='build.Build'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0011_auto_20190908_0404.py b/src/backend/InvenTree/stock/migrations/0011_auto_20190908_0404.py deleted file mode 100644 index 7fc85d0556..0000000000 --- a/src/backend/InvenTree/stock/migrations/0011_auto_20190908_0404.py +++ /dev/null @@ -1,37 +0,0 @@ -# Generated by Django 2.2.5 on 2019-09-08 04:04 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0010_stockitem_build'), - ] - - operations = [ - migrations.AddField( - model_name='stocklocation', - name='level', - field=models.PositiveIntegerField(default=0, editable=False), - preserve_default=False, - ), - migrations.AddField( - model_name='stocklocation', - name='lft', - field=models.PositiveIntegerField(default=0, editable=False), - preserve_default=False, - ), - migrations.AddField( - model_name='stocklocation', - name='rght', - field=models.PositiveIntegerField(default=0, editable=False), - preserve_default=False, - ), - migrations.AddField( - model_name='stocklocation', - name='tree_id', - field=models.PositiveIntegerField(db_index=True, default=0, editable=False), - preserve_default=False, - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0012_auto_20190908_0405.py b/src/backend/InvenTree/stock/migrations/0012_auto_20190908_0405.py deleted file mode 100644 index 699d3d4f90..0000000000 --- a/src/backend/InvenTree/stock/migrations/0012_auto_20190908_0405.py +++ /dev/null @@ -1,15 +0,0 @@ -# Generated by Django 2.2.5 on 2019-09-08 04:05 - -from django.db import migrations - - - -class Migration(migrations.Migration): - - atomic = False - - dependencies = [ - ('stock', '0011_auto_20190908_0404'), - ] - - operations = [] diff --git a/src/backend/InvenTree/stock/migrations/0013_auto_20190908_0916.py b/src/backend/InvenTree/stock/migrations/0013_auto_20190908_0916.py deleted file mode 100644 index 897d4298c2..0000000000 --- a/src/backend/InvenTree/stock/migrations/0013_auto_20190908_0916.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2.5 on 2019-09-08 09:16 - -from django.db import migrations -import django.db.models.deletion -import mptt.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0012_auto_20190908_0405'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='location', - field=mptt.fields.TreeForeignKey(blank=True, help_text='Where is this stock item located?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='stock_items', to='stock.StockLocation'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0014_auto_20190908_0918.py b/src/backend/InvenTree/stock/migrations/0014_auto_20190908_0918.py deleted file mode 100644 index b6f7411e10..0000000000 --- a/src/backend/InvenTree/stock/migrations/0014_auto_20190908_0918.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 2.2.5 on 2019-09-08 09:18 - -from django.db import migrations -import django.db.models.deletion -import mptt.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0013_auto_20190908_0916'), - ] - - operations = [ - migrations.AlterField( - model_name='stocklocation', - name='parent', - field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='stock.StockLocation'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0015_auto_20190913_1407.py b/src/backend/InvenTree/stock/migrations/0015_auto_20190913_1407.py deleted file mode 100644 index de1b49a2a2..0000000000 --- a/src/backend/InvenTree/stock/migrations/0015_auto_20190913_1407.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 2.2.5 on 2019-09-13 14:07 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0014_auto_20190908_0918'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='URL', - field=InvenTree.fields.InvenTreeURLField(blank=True, max_length=125), - ), - migrations.AlterField( - model_name='stockitemtracking', - name='URL', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page for further information'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0016_auto_20191118_2146.py b/src/backend/InvenTree/stock/migrations/0016_auto_20191118_2146.py deleted file mode 100644 index d3dbe957ed..0000000000 --- a/src/backend/InvenTree/stock/migrations/0016_auto_20191118_2146.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.5 on 2019-11-18 21:46 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0015_auto_20190913_1407'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='quantity', - field=models.DecimalField(decimal_places=5, default=1, max_digits=15, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0017_auto_20191118_2311.py b/src/backend/InvenTree/stock/migrations/0017_auto_20191118_2311.py deleted file mode 100644 index 32e6de6cea..0000000000 --- a/src/backend/InvenTree/stock/migrations/0017_auto_20191118_2311.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.5 on 2019-11-18 23:11 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0016_auto_20191118_2146'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitemtracking', - name='quantity', - field=models.DecimalField(decimal_places=5, default=1, max_digits=15, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0018_auto_20200202_0103.py b/src/backend/InvenTree/stock/migrations/0018_auto_20200202_0103.py deleted file mode 100644 index 50bccf6e19..0000000000 --- a/src/backend/InvenTree/stock/migrations/0018_auto_20200202_0103.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.9 on 2020-02-02 01:03 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0017_auto_20191118_2311'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='notes', - field=models.TextField(blank=True, help_text='Stock Item Notes'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0019_auto_20200202_1024.py b/src/backend/InvenTree/stock/migrations/0019_auto_20200202_1024.py deleted file mode 100644 index 8dc67afb61..0000000000 --- a/src/backend/InvenTree/stock/migrations/0019_auto_20200202_1024.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.9 on 2020-02-02 10:24 - -import InvenTree.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0018_auto_20200202_0103'), - ] - - operations = [ - migrations.AlterField( - model_name='stocklocation', - name='name', - field=models.CharField(max_length=100, validators=[InvenTree.validators.validate_tree_name]), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0020_auto_20200206_1213.py b/src/backend/InvenTree/stock/migrations/0020_auto_20200206_1213.py deleted file mode 100644 index e36bc6a0cd..0000000000 --- a/src/backend/InvenTree/stock/migrations/0020_auto_20200206_1213.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.9 on 2020-02-06 12:13 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0019_auto_20200202_1024'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='notes', - field=models.TextField(blank=True, help_text='Stock Item Notes', null=True), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0021_auto_20200215_2232.py b/src/backend/InvenTree/stock/migrations/0021_auto_20200215_2232.py deleted file mode 100644 index 3ecca4d6f5..0000000000 --- a/src/backend/InvenTree/stock/migrations/0021_auto_20200215_2232.py +++ /dev/null @@ -1,44 +0,0 @@ -# Generated by Django 2.2.9 on 2020-02-15 22:32 - -from django.db import migrations, models -import django.db.models.deletion -import mptt.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0020_auto_20200206_1213'), - ] - - operations = [ - migrations.AddField( - model_name='stockitem', - name='level', - field=models.PositiveIntegerField(default=0, editable=False), - preserve_default=False, - ), - migrations.AddField( - model_name='stockitem', - name='lft', - field=models.PositiveIntegerField(default=0, editable=False), - preserve_default=False, - ), - migrations.AddField( - model_name='stockitem', - name='parent', - field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='stock.StockItem'), - ), - migrations.AddField( - model_name='stockitem', - name='rght', - field=models.PositiveIntegerField(default=0, editable=False), - preserve_default=False, - ), - migrations.AddField( - model_name='stockitem', - name='tree_id', - field=models.PositiveIntegerField(db_index=True, default=0, editable=False), - preserve_default=False, - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0022_auto_20200217_1109.py b/src/backend/InvenTree/stock/migrations/0022_auto_20200217_1109.py deleted file mode 100644 index e42c1bcdd2..0000000000 --- a/src/backend/InvenTree/stock/migrations/0022_auto_20200217_1109.py +++ /dev/null @@ -1,14 +0,0 @@ -# Generated by Django 2.2.9 on 2020-02-17 11:09 - -from django.db import migrations - - -class Migration(migrations.Migration): - - atomic = False - - dependencies = [ - ('stock', '0021_auto_20200215_2232'), - ] - - operations = [] diff --git a/src/backend/InvenTree/stock/migrations/0023_auto_20200318_1027.py b/src/backend/InvenTree/stock/migrations/0023_auto_20200318_1027.py deleted file mode 100644 index 268bdd6cf2..0000000000 --- a/src/backend/InvenTree/stock/migrations/0023_auto_20200318_1027.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.9 on 2020-03-18 10:27 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0022_auto_20200217_1109'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='part', - field=models.ForeignKey(help_text='Base part', limit_choices_to={'active': True, 'is_template': False, 'virtual': False}, on_delete=django.db.models.deletion.CASCADE, related_name='stock_items', to='part.Part'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0024_auto_20200405_2239.py b/src/backend/InvenTree/stock/migrations/0024_auto_20200405_2239.py deleted file mode 100644 index c285b4a813..0000000000 --- a/src/backend/InvenTree/stock/migrations/0024_auto_20200405_2239.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.10 on 2020-04-05 22:39 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0023_auto_20200318_1027'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='URL', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=125), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0025_auto_20200405_2243.py b/src/backend/InvenTree/stock/migrations/0025_auto_20200405_2243.py deleted file mode 100644 index 0eab1c813b..0000000000 --- a/src/backend/InvenTree/stock/migrations/0025_auto_20200405_2243.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 2.2.10 on 2020-04-05 22:43 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0024_auto_20200405_2239'), - ] - - operations = [ - migrations.RenameField( - model_name='stockitem', - old_name='URL', - new_name='link', - ), - migrations.RenameField( - model_name='stockitemtracking', - old_name='URL', - new_name='link', - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0026_stockitem_uid.py b/src/backend/InvenTree/stock/migrations/0026_stockitem_uid.py deleted file mode 100644 index c00e858815..0000000000 --- a/src/backend/InvenTree/stock/migrations/0026_stockitem_uid.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.10 on 2020-04-14 12:53 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0025_auto_20200405_2243'), - ] - - operations = [ - migrations.AddField( - model_name='stockitem', - name='uid', - field=models.CharField(blank=True, help_text='Unique identifier field', max_length=128), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0027_stockitem_sales_order.py b/src/backend/InvenTree/stock/migrations/0027_stockitem_sales_order.py deleted file mode 100644 index 048609ae5f..0000000000 --- a/src/backend/InvenTree/stock/migrations/0027_stockitem_sales_order.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.0.5 on 2020-04-21 05:03 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0023_auto_20200420_2309'), - ('stock', '0026_stockitem_uid'), - ] - - operations = [ - migrations.AddField( - model_name='stockitem', - name='sales_order', - field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stock_items', to='order.SalesOrderLineItem'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0028_auto_20200421_0724.py b/src/backend/InvenTree/stock/migrations/0028_auto_20200421_0724.py deleted file mode 100644 index 61ebe97039..0000000000 --- a/src/backend/InvenTree/stock/migrations/0028_auto_20200421_0724.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.5 on 2020-04-21 07:24 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0027_stockitem_sales_order'), - ] - - operations = [ - migrations.RenameField( - model_name='stockitem', - old_name='sales_order', - new_name='sales_order_line', - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0029_auto_20200421_2359.py b/src/backend/InvenTree/stock/migrations/0029_auto_20200421_2359.py deleted file mode 100644 index 1b89a9d143..0000000000 --- a/src/backend/InvenTree/stock/migrations/0029_auto_20200421_2359.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.0.5 on 2020-04-21 23:59 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0028_auto_20200421_0724'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='status', - field=models.PositiveIntegerField(choices=[(10, 'OK'), (50, 'Attention needed'), (55, 'Damaged'), (60, 'Destroyed'), (70, 'Lost'), (110, 'Shipped'), (85, 'Returned')], default=10, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0030_auto_20200422_0015.py b/src/backend/InvenTree/stock/migrations/0030_auto_20200422_0015.py deleted file mode 100644 index c720ac48ef..0000000000 --- a/src/backend/InvenTree/stock/migrations/0030_auto_20200422_0015.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.0.5 on 2020-04-22 00:15 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('order', '0023_auto_20200420_2309'), - ('stock', '0029_auto_20200421_2359'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='sales_order_line', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stock_items', to='order.SalesOrderLineItem'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0031_auto_20200422_0209.py b/src/backend/InvenTree/stock/migrations/0031_auto_20200422_0209.py index 1da143aac5..cc14dc47a4 100644 --- a/src/backend/InvenTree/stock/migrations/0031_auto_20200422_0209.py +++ b/src/backend/InvenTree/stock/migrations/0031_auto_20200422_0209.py @@ -8,7 +8,7 @@ class Migration(migrations.Migration): dependencies = [ ('order', '0024_salesorderallocation'), - ('stock', '0030_auto_20200422_0015'), + ('stock', '0002_auto_20190525_2226_squashed_0030_auto_20200422_0015'), ] operations = [ diff --git a/src/backend/InvenTree/stock/migrations/0059_auto_20210404_2016.py b/src/backend/InvenTree/stock/migrations/0059_auto_20210404_2016.py deleted file mode 100644 index 3305b719e5..0000000000 --- a/src/backend/InvenTree/stock/migrations/0059_auto_20210404_2016.py +++ /dev/null @@ -1,92 +0,0 @@ -# Generated by Django 3.0.7 on 2021-04-04 20:16 - -import InvenTree.fields -import InvenTree.models -import InvenTree.validators -from django.conf import settings -import django.core.validators -from django.db import migrations, models -import django.db.models.deletion -import mptt.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0005_owner_model'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('stock', '0058_stockitem_packaging'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='delete_on_deplete', - field=models.BooleanField(default=True, help_text='Delete this Stock Item when stock is depleted', verbose_name='Delete on deplete'), - ), - migrations.AlterField( - model_name='stockitem', - name='owner', - field=models.ForeignKey(blank=True, help_text='Select Owner', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stock_items', to='users.Owner', verbose_name='Owner'), - ), - migrations.AlterField( - model_name='stockitemattachment', - name='attachment', - field=models.FileField(help_text='Select file to attach', upload_to='attachments', verbose_name='Attachment'), - ), - migrations.AlterField( - model_name='stockitemattachment', - name='comment', - field=models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment'), - ), - migrations.AlterField( - model_name='stockitemattachment', - name='upload_date', - field=models.DateField(auto_now_add=True, null=True, verbose_name='upload date'), - ), - migrations.AlterField( - model_name='stockitemattachment', - name='user', - field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User'), - ), - migrations.AlterField( - model_name='stockitemtracking', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page for further information', verbose_name='Link'), - ), - migrations.AlterField( - model_name='stockitemtracking', - name='notes', - field=models.CharField(blank=True, help_text='Entry notes', max_length=512, verbose_name='Notes'), - ), - migrations.AlterField( - model_name='stockitemtracking', - name='quantity', - field=models.DecimalField(decimal_places=5, default=1, max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'), - ), - migrations.AlterField( - model_name='stockitemtracking', - name='title', - field=models.CharField(help_text='Tracking entry title', max_length=250, verbose_name='Title'), - ), - migrations.AlterField( - model_name='stocklocation', - name='description', - field=models.CharField(blank=True, help_text='Description (optional)', max_length=250, verbose_name='Description'), - ), - migrations.AlterField( - model_name='stocklocation', - name='name', - field=models.CharField(help_text='Name', max_length=100, validators=[InvenTree.validators.validate_tree_name], verbose_name='Name'), - ), - migrations.AlterField( - model_name='stocklocation', - name='owner', - field=models.ForeignKey(blank=True, help_text='Select Owner', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stock_locations', to='users.Owner', verbose_name='Owner'), - ), - migrations.AlterField( - model_name='stocklocation', - name='parent', - field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='stock.StockLocation', verbose_name='parent'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0059_auto_20210404_2016_squashed_0116_alter_stockitem_link.py b/src/backend/InvenTree/stock/migrations/0059_auto_20210404_2016_squashed_0116_alter_stockitem_link.py new file mode 100644 index 0000000000..9cc244b84c --- /dev/null +++ b/src/backend/InvenTree/stock/migrations/0059_auto_20210404_2016_squashed_0116_alter_stockitem_link.py @@ -0,0 +1,405 @@ +# Generated by Django 5.2.17 on 2026-09-09 00:59 + +import InvenTree.fields +import InvenTree.validators +import common.currency +import common.icons +import django.core.validators +import django.db.models.deletion +import djmoney.models.fields +import djmoney.models.validators +import generic.states.fields +import generic.states.validators +import mptt.fields +import stock.generators +import stock.models +import stock.status_codes +import taggit.managers +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + replaces = [('stock', '0059_auto_20210404_2016'), ('stock', '0060_auto_20210511_1713'), ('stock', '0061_auto_20210511_0911'), ('stock', '0062_auto_20210511_2151'), ('stock', '0063_auto_20210511_2343'), ('stock', '0064_auto_20210621_1724'), ('stock', '0065_auto_20210701_0509'), ('stock', '0066_stockitem_scheduled_for_deletion'), ('stock', '0067_alter_stockitem_part'), ('stock', '0068_stockitem_serial_int'), ('stock', '0069_auto_20211109_2347'), ('stock', '0070_auto_20211128_0151'), ('stock', '0071_auto_20211205_1733'), ('stock', '0072_remove_stockitem_scheduled_for_deletion'), ('stock', '0073_alter_stockitem_belongs_to'), ('stock', '0074_alter_stockitem_batch'), ('stock', '0075_auto_20220515_1440'), ('stock', '0076_alter_stockitem_status'), ('stock', '0077_alter_stockitem_notes'), ('stock', '0078_alter_stockitem_supplier_part'), ('stock', '0079_alter_stocklocation_name'), ('stock', '0080_stocklocation_pathstring'), ('stock', '0081_auto_20220801_0044'), ('stock', '0082_alter_stockitem_link'), ('stock', '0083_stocklocation_icon'), ('stock', '0084_auto_20220903_0154'), ('stock', '0085_auto_20220903_0225'), ('stock', '0086_remove_stockitem_uid'), ('stock', '0087_auto_20220912_2341'), ('stock', '0088_remove_stockitem_infinite'), ('stock', '0089_alter_stockitem_purchase_price'), ('stock', '0090_stocklocation_structural'), ('stock', '0091_alter_stockitem_delete_on_deplete'), ('stock', '0092_alter_stockitem_updated'), ('stock', '0093_auto_20230217_2140'), ('stock', '0094_auto_20230220_0025'), ('stock', '0095_stocklocation_external'), ('stock', '0096_auto_20230330_1121'), ('stock', '0097_alter_stockitem_notes'), ('stock', '0098_auto_20230427_2033'), ('stock', '0099_alter_stockitem_status'), ('stock', '0100_auto_20230515_0004'), ('stock', '0100_stockitem_consumed_by'), ('stock', '0101_stockitemtestresult_metadata'), ('stock', '0102_alter_stockitem_status'), ('stock', '0103_stock_location_types'), ('stock', '0104_alter_stockitem_purchase_price_currency'), ('stock', '0105_stockitemtestresult_template'), ('stock', '0106_auto_20240207_0353'), ('stock', '0107_remove_stockitemtestresult_test_and_more'), ('stock', '0108_auto_20240219_0252'), ('stock', '0109_add_additional_test_fields'), ('stock', '0110_alter_stockitemtestresult_finished_datetime_and_more'), ('stock', '0111_delete_stockitemattachment'), ('stock', '0112_alter_stocklocation_custom_icon_and_more'), ('stock', '0113_stockitem_status_custom_key_and_more'), ('stock', '0114_alter_stocklocation_custom_icon'), ('stock', '0115_auto_20250221_1323'), ('stock', '0116_alter_stockitem_link')] + + dependencies = [ + ('company', '0001_initial'), + ('django_q', '0007_ormq'), + ('part', '0074_partcategorystar'), + ('part', '0121_auto_20240207_0344'), + ('stock', '0058_stockitem_packaging'), + ('taggit', '0005_auto_20220424_2025'), + ('users', '0005_owner_model'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name='stockitem', + name='delete_on_deplete', + field=models.BooleanField(default=True, help_text='Delete this Stock Item when stock is depleted', verbose_name='Delete on deplete'), + ), + migrations.AlterField( + model_name='stockitem', + name='owner', + field=models.ForeignKey(blank=True, help_text='Select Owner', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stock_items', to='users.owner', verbose_name='Owner'), + ), + migrations.AlterField( + model_name='stockitemattachment', + name='attachment', + field=models.FileField(help_text='Select file to attach', upload_to='attachments', verbose_name='Attachment'), + ), + migrations.AlterField( + model_name='stockitemattachment', + name='comment', + field=models.CharField(blank=True, help_text='File comment', max_length=100, verbose_name='Comment'), + ), + migrations.AlterField( + model_name='stockitemattachment', + name='upload_date', + field=models.DateField(auto_now_add=True, null=True, verbose_name='upload date'), + ), + migrations.AlterField( + model_name='stockitemattachment', + name='user', + field=models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='User'), + ), + migrations.AlterField( + model_name='stockitemtracking', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external page for further information', max_length=2000, verbose_name='Link'), + ), + migrations.AlterField( + model_name='stockitemtracking', + name='notes', + field=models.CharField(blank=True, help_text='Entry notes', max_length=512, verbose_name='Notes'), + ), + migrations.AlterField( + model_name='stockitemtracking', + name='quantity', + field=models.DecimalField(decimal_places=5, default=1, max_digits=15, validators=[django.core.validators.MinValueValidator(0)], verbose_name='Quantity'), + ), + migrations.AlterField( + model_name='stocklocation', + name='description', + field=models.CharField(blank=True, help_text='Description (optional)', max_length=250, verbose_name='Description'), + ), + migrations.AlterField( + model_name='stocklocation', + name='name', + field=models.CharField(help_text='Name', max_length=100, validators=[InvenTree.validators.validate_tree_name], verbose_name='Name'), + ), + migrations.AlterField( + model_name='stocklocation', + name='owner', + field=models.ForeignKey(blank=True, help_text='Select Owner', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stock_locations', to='users.owner', verbose_name='Owner'), + ), + migrations.AlterField( + model_name='stocklocation', + name='parent', + field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='stock.stocklocation', verbose_name='parent'), + ), + migrations.AddField( + model_name='stockitemtracking', + name='deltas', + field=models.JSONField(blank=True, null=True), + ), + migrations.AddField( + model_name='stockitemtracking', + name='tracking_type', + field=models.IntegerField(default=0), + ), + migrations.AlterField( + model_name='stockitemtracking', + name='title', + field=models.CharField(blank=True, help_text='Tracking entry title', max_length=250, verbose_name='Title'), + ), + migrations.AlterField( + model_name='stockitemtracking', + name='notes', + field=models.CharField(blank=True, help_text='Entry notes', max_length=512, null=True, verbose_name='Notes'), + ), + migrations.RemoveField( + model_name='stockitemtracking', + name='link', + ), + migrations.RemoveField( + model_name='stockitemtracking', + name='quantity', + ), + migrations.RemoveField( + model_name='stockitemtracking', + name='system', + ), + migrations.RemoveField( + model_name='stockitemtracking', + name='title', + ), + migrations.AddField( + model_name='stockitem', + name='scheduled_for_deletion', + field=models.BooleanField(default=False, help_text='This StockItem will be deleted by the background worker', verbose_name='Scheduled for deletion'), + ), + migrations.AlterField( + model_name='stockitem', + name='part', + field=models.ForeignKey(help_text='Base part', limit_choices_to={'virtual': False}, on_delete=django.db.models.deletion.CASCADE, related_name='stock_items', to='part.part', verbose_name='Base Part'), + ), + migrations.AddField( + model_name='stockitem', + name='serial_int', + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name='stockitemattachment', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=2000, null=True, verbose_name='Link'), + ), + migrations.AlterField( + model_name='stockitemattachment', + name='attachment', + field=models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to='attachments', verbose_name='Attachment'), + ), + migrations.RemoveField( + model_name='stockitem', + name='scheduled_for_deletion', + ), + migrations.AlterField( + model_name='stockitem', + name='belongs_to', + field=models.ForeignKey(blank=True, help_text='Is this item installed in another item?', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='installed_parts', to='stock.stockitem', verbose_name='Installed In'), + ), + migrations.AlterField( + model_name='stockitem', + name='batch', + field=models.CharField(blank=True, default=stock.generators.generate_batch_code, help_text='Batch code for this stock item', max_length=100, null=True, verbose_name='Batch Code'), + ), + migrations.AddField( + model_name='stockitem', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AddField( + model_name='stocklocation', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AlterUniqueTogether( + name='stocklocation', + unique_together=set(), + ), + migrations.AlterField( + model_name='stockitem', + name='status', + field=models.PositiveIntegerField(choices=[(10, 'OK'), (50, 'Attention needed'), (55, 'Damaged'), (60, 'Destroyed'), (70, 'Lost'), (65, 'Rejected'), (75, 'Quarantined'), (85, 'Returned')], default=10, validators=[django.core.validators.MinValueValidator(0)]), + ), + migrations.AlterField( + model_name='stockitem', + name='notes', + field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Stock Item Notes', max_length=50000, null=True, verbose_name='Notes'), + ), + migrations.AlterField( + model_name='stockitem', + name='supplier_part', + field=models.ForeignKey(blank=True, help_text='Select a matching supplier part for this stock item', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stock_items', to='company.supplierpart', verbose_name='Supplier Part'), + ), + migrations.AlterField( + model_name='stocklocation', + name='name', + field=models.CharField(help_text='Name', max_length=100, verbose_name='Name'), + ), + migrations.AddField( + model_name='stocklocation', + name='pathstring', + field=models.CharField(blank=True, help_text='Path', max_length=250, verbose_name='Path'), + ), + migrations.AlterField( + model_name='stockitem', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=2000, verbose_name='External Link'), + ), + migrations.AddField( + model_name='stocklocation', + name='icon', + field=models.CharField(blank=True, help_text='Icon (optional)', max_length=100, verbose_name='Icon'), + ), + migrations.AddField( + model_name='stockitem', + name='barcode_data', + field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), + ), + migrations.AddField( + model_name='stockitem', + name='barcode_hash', + field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), + ), + migrations.RemoveField( + model_name='stockitem', + name='uid', + ), + migrations.AddField( + model_name='stocklocation', + name='barcode_data', + field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), + ), + migrations.AddField( + model_name='stocklocation', + name='barcode_hash', + field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), + ), + migrations.RemoveField( + model_name='stockitem', + name='infinite', + ), + migrations.AlterField( + model_name='stockitem', + name='purchase_price', + field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency=common.currency.currency_code_default, help_text='Single unit purchase price at time of purchase', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Purchase Price'), + ), + migrations.AddField( + model_name='stocklocation', + name='structural', + field=models.BooleanField(default=False, help_text='Stock items may not be directly located into a structural stock locations, but may be located to child locations.', verbose_name='Structural'), + ), + migrations.AlterField( + model_name='stockitem', + name='delete_on_deplete', + field=models.BooleanField(default=stock.models.default_delete_on_deplete, help_text='Delete this Stock Item when stock is depleted', verbose_name='Delete on deplete'), + ), + migrations.AlterField( + model_name='stockitem', + name='updated', + field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'), + ), + migrations.AlterModelOptions( + name='stocklocation', + options={'verbose_name': 'Stock Location', 'verbose_name_plural': 'Stock Locations'}, + ), + migrations.AddField( + model_name='stocklocation', + name='external', + field=models.BooleanField(default=False, help_text='This is an external stock location', verbose_name='External'), + ), + migrations.AlterField( + model_name='stockitem', + name='notes', + field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), + ), + migrations.AddField( + model_name='stockitem', + name='tags', + field=taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), + ), + migrations.AddField( + model_name='stocklocation', + name='tags', + field=taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), + ), + migrations.AddField( + model_name='stockitem', + name='consumed_by', + field=models.ForeignKey(blank=True, help_text='Build order which consumed this stock item', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='consumed_stock', to='build.build', verbose_name='Consumed By'), + ), + migrations.AddField( + model_name='stockitemtestresult', + name='metadata', + field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), + ), + migrations.AlterField( + model_name='stockitem', + name='status', + field=models.PositiveIntegerField(choices=[(10, 'OK'), (50, 'Attention needed'), (55, 'Damaged'), (60, 'Destroyed'), (65, 'Rejected'), (70, 'Lost'), (75, 'Quarantined'), (85, 'Returned')], default=10, validators=[django.core.validators.MinValueValidator(0)]), + ), + migrations.CreateModel( + name='StockLocationType', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('metadata', models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata')), + ('name', models.CharField(help_text='Name', max_length=100, verbose_name='Name')), + ('description', models.CharField(blank=True, help_text='Description (optional)', max_length=250, verbose_name='Description')), + ('icon', models.CharField(blank=True, help_text='Default icon for all locations that have no icon set (optional)', max_length=100, verbose_name='Icon')), + ], + options={ + 'verbose_name': 'Stock Location type', + 'verbose_name_plural': 'Stock Location types', + }, + ), + migrations.RenameField( + model_name='stocklocation', + old_name='icon', + new_name='custom_icon', + ), + migrations.AlterField( + model_name='stocklocation', + name='custom_icon', + field=models.CharField(blank=True, db_column='icon', help_text='Icon (optional)', max_length=100, verbose_name='Icon'), + ), + migrations.AddField( + model_name='stocklocation', + name='location_type', + field=models.ForeignKey(blank=True, help_text='Stock location type of this location', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stock_locations', to='stock.stocklocationtype', verbose_name='Location type'), + ), + migrations.AlterField( + model_name='stockitem', + name='purchase_price_currency', + field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), + ), + migrations.AddField( + model_name='stockitemtestresult', + name='template', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='test_results', to='part.parttesttemplate'), + ), + migrations.RemoveField( + model_name='stockitemtestresult', + name='test', + ), + migrations.AlterField( + model_name='stockitemtestresult', + name='template', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='test_results', to='part.parttesttemplate'), + ), + migrations.AddField( + model_name='stockitemtestresult', + name='test_station', + field=models.CharField(blank=True, help_text='The identifier of the test station where the test was performed', max_length=500, verbose_name='Test station'), + ), + migrations.AddField( + model_name='stockitemtestresult', + name='finished_datetime', + field=models.DateTimeField(blank=True, help_text='The timestamp of the test finish', null=True, verbose_name='Finished'), + ), + migrations.AddField( + model_name='stockitemtestresult', + name='started_datetime', + field=models.DateTimeField(blank=True, help_text='The timestamp of the test start', null=True, verbose_name='Started'), + ), + migrations.DeleteModel( + name='StockItemAttachment', + ), + migrations.AlterField( + model_name='stocklocationtype', + name='icon', + field=models.CharField(blank=True, help_text='Default icon for all locations that have no icon set (optional)', max_length=100, validators=[common.icons.validate_icon], verbose_name='Icon'), + ), + migrations.AddField( + model_name='stockitem', + name='status_custom_key', + field=generic.states.fields.ExtraInvenTreeCustomStatusModelField(blank=True, default=None, help_text='Additional status information for this item', null=True, validators=[generic.states.validators.CustomStatusCodeValidator(status_class=stock.status_codes.StockStatus)], verbose_name='Custom status key'), + ), + migrations.AlterField( + model_name='stockitem', + name='status', + field=generic.states.fields.InvenTreeCustomStatusModelField(choices=[(10, 'OK'), (50, 'Attention needed'), (55, 'Damaged'), (60, 'Destroyed'), (65, 'Rejected'), (70, 'Lost'), (75, 'Quarantined'), (85, 'Returned')], default=10, validators=[django.core.validators.MinValueValidator(0), generic.states.validators.CustomStatusCodeValidator(status_class=stock.status_codes.StockStatus)]), + ), + migrations.AlterField( + model_name='stocklocation', + name='custom_icon', + field=models.CharField(blank=True, db_column='icon', help_text='Icon (optional)', max_length=100, null=True, validators=[common.icons.validate_icon], verbose_name='Icon'), + ), + migrations.AlterField( + model_name='stockitem', + name='link', + field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=2000, verbose_name='External Link'), + ), + ] diff --git a/src/backend/InvenTree/stock/migrations/0060_auto_20210511_1713.py b/src/backend/InvenTree/stock/migrations/0060_auto_20210511_1713.py deleted file mode 100644 index 752b070750..0000000000 --- a/src/backend/InvenTree/stock/migrations/0060_auto_20210511_1713.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 3.2 on 2021-05-11 07:13 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0059_auto_20210404_2016'), - ] - - operations = [ - migrations.AddField( - model_name='stockitemtracking', - name='deltas', - field=models.JSONField(blank=True, null=True), - ), - migrations.AddField( - model_name='stockitemtracking', - name='tracking_type', - field=models.IntegerField(default=0), - ), - migrations.AlterField( - model_name='stockitemtracking', - name='title', - field=models.CharField(blank=True, help_text='Tracking entry title', max_length=250, verbose_name='Title'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0061_auto_20210511_0911.py b/src/backend/InvenTree/stock/migrations/0061_auto_20210511_0911.py deleted file mode 100644 index e5d2b82399..0000000000 --- a/src/backend/InvenTree/stock/migrations/0061_auto_20210511_0911.py +++ /dev/null @@ -1,214 +0,0 @@ -# Generated by Django 3.2 on 2021-05-10 23:11 - -import re - -from django.db import migrations - -from stock.status_codes import StockHistoryCode - - -def update_history(apps, schema_editor): - """ - Update each existing StockItemTracking object, - convert the recorded "quantity" to a delta - """ - - StockItem = apps.get_model('stock', 'stockitem') - StockItemTracking = apps.get_model('stock', 'stockitemtracking') - StockLocation = apps.get_model('stock', 'stocklocation') - - update_count = 0 - - locations = StockLocation.objects.all() - - for location in locations: # pragma: no cover - # Pre-calculate pathstring - # Note we cannot use the 'pathstring' function here as we don't have access to model functions! - - path = [location.name] - - loc = location - - while loc.parent: - loc = loc.parent - path = [loc.name] + path - - location._path = '/'.join(path) - - for item in StockItem.objects.all(): # pragma: no cover - - history = StockItemTracking.objects.filter(item=item).order_by('date') - - if history.count() == 0: - continue - - quantity = history[0].quantity - - for idx, entry in enumerate(history): - - deltas = {} - updated = False - - q = entry.quantity - - if idx == 0 or q != quantity: - - try: - deltas['quantity']= float(q) - updated = True - except Exception: - print(f"WARNING: Error converting quantity '{q}'") - - - quantity = q - - # Try to "guess" the "type" of tracking entry, based on the title - title = entry.title.lower() - - tracking_type = None - - if 'completed build' in title: - tracking_type = StockHistoryCode.BUILD_OUTPUT_COMPLETED - - elif 'removed' in title and 'item' in title: - - if entry.notes.lower().startswith('split '): - tracking_type = StockHistoryCode.SPLIT_CHILD_ITEM - else: - tracking_type = StockHistoryCode.STOCK_REMOVE - - # Extract the number of removed items - result = re.search(r"^removed ([\d\.]+) items", title) - - if result: - - removed = result.groups()[0] - - try: - deltas['removed'] = float(removed) - - # Ensure that 'quantity' is stored too in this case - deltas['quantity'] = float(q) - except Exception: - print(f"WARNING: Error converting removed quantity '{removed}'") - else: - print(f"Could not decode '{title}'") - - elif 'split from existing' in title: - tracking_type = StockHistoryCode.SPLIT_FROM_PARENT - - deltas['quantity'] = float(q) - - elif 'moved to' in title: - tracking_type = StockHistoryCode.STOCK_MOVE - - result = re.search(r'^Moved to (.*)( - )*(.*) \(from.*$', entry.title) - - if result: - # Legacy tracking entries recorded the location in multiple ways, because.. why not? - text = result.groups()[0] - - matches = set() - - for location in locations: - - # Direct match for pathstring - if text == location._path: - matches.add(location) - - # Direct match for name - if text == location.name: - matches.add(location) - - # Match for "name - description" - compare = f"{location.name} - {location.description}" - - if text == compare: - matches.add(location) - - # Match for "pathstring - description" - compare = f"{location._path} - {location.description}" - - if text == compare: - matches.add(location) - - if len(matches) == 1: - location = list(matches)[0] - - deltas['location'] = location.pk - - else: - print(f"No location match: '{text}'") - break - - elif 'created stock item' in title: - tracking_type = StockHistoryCode.CREATED - - elif 'add serial number' in title: - tracking_type = StockHistoryCode.ASSIGNED_SERIAL - - elif 'returned from customer' in title: - tracking_type = StockHistoryCode.RETURNED_FROM_CUSTOMER - - elif 'counted' in title: - tracking_type = StockHistoryCode.STOCK_COUNT - - elif 'added' in title: - tracking_type = StockHistoryCode.STOCK_ADD - - # Extract the number of added items - result = re.search(r"^added ([\d\.]+) items", title) - - if result: - - added = result.groups()[0] - - try: - deltas['added'] = float(added) - - # Ensure that 'quantity' is stored too in this case - deltas['quantity'] = float(q) - except Exception: - print(f"WARNING: Error converting added quantity '{added}'") - - else: - print(f"Could not decode '{title}'") - - elif 'assigned to customer' in title: - tracking_type = StockHistoryCode.SENT_TO_CUSTOMER - - elif 'installed into stock item' in title: - tracking_type = StockHistoryCode.INSTALLED_INTO_ASSEMBLY - - elif 'uninstalled into location' in title: - tracking_type = StockHistoryCode.REMOVED_FROM_ASSEMBLY - - elif 'installed stock item' in title: - tracking_type = StockHistoryCode.INSTALLED_CHILD_ITEM - - elif 'received items' in title: - tracking_type = StockHistoryCode.RECEIVED_AGAINST_PURCHASE_ORDER - - if tracking_type is not None: - entry.tracking_type = tracking_type.value - updated = True - - if updated: - entry.deltas = deltas - entry.save() - update_count += 1 - - - if update_count > 0: - print(f"\n==========================\nUpdated {update_count} StockItemHistory entries") # pragma: no cover - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0060_auto_20210511_1713'), - ] - - operations = [ - migrations.RunPython(update_history, reverse_code=migrations.RunPython.noop) - ] diff --git a/src/backend/InvenTree/stock/migrations/0062_auto_20210511_2151.py b/src/backend/InvenTree/stock/migrations/0062_auto_20210511_2151.py deleted file mode 100644 index 18832819ff..0000000000 --- a/src/backend/InvenTree/stock/migrations/0062_auto_20210511_2151.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2 on 2021-05-11 11:51 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0061_auto_20210511_0911'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitemtracking', - name='notes', - field=models.CharField(blank=True, help_text='Entry notes', max_length=512, null=True, verbose_name='Notes'), - ), - migrations.AlterField( - model_name='stockitemtracking', - name='title', - field=models.CharField(blank=True, help_text='Tracking entry title', max_length=250, null=True, verbose_name='Title'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0063_auto_20210511_2343.py b/src/backend/InvenTree/stock/migrations/0063_auto_20210511_2343.py deleted file mode 100644 index dc8a391cde..0000000000 --- a/src/backend/InvenTree/stock/migrations/0063_auto_20210511_2343.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 3.2 on 2021-05-11 13:43 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0062_auto_20210511_2151'), - ] - - operations = [ - migrations.RemoveField( - model_name='stockitemtracking', - name='link', - ), - migrations.RemoveField( - model_name='stockitemtracking', - name='quantity', - ), - migrations.RemoveField( - model_name='stockitemtracking', - name='system', - ), - migrations.RemoveField( - model_name='stockitemtracking', - name='title', - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0064_auto_20210621_1724.py b/src/backend/InvenTree/stock/migrations/0064_auto_20210621_1724.py deleted file mode 100644 index 32ccba7d51..0000000000 --- a/src/backend/InvenTree/stock/migrations/0064_auto_20210621_1724.py +++ /dev/null @@ -1,72 +0,0 @@ -# Generated by Django 3.2.4 on 2021-06-21 07:24 - -from django.db import migrations - - -def extract_purchase_price(apps, schema_editor): - """ - Find instances of StockItem which do *not* have a purchase price set, - but which point to a PurchaseOrder where there *is* a purchase price set. - - Then, assign *that* purchase price to original StockItem. - - This is to address an issue where older versions of InvenTree - did not correctly copy purchase price information cross to the StockItem objects. - - Current InvenTree version (as of 2021-06-21) copy this information across correctly, - so this one-time data migration should suffice. - """ - - # Required database models - StockItem = apps.get_model('stock', 'stockitem') - PurchaseOrder = apps.get_model('order', 'purchaseorder') - PurchaseOrderLineItem = apps.get_model('order', 'purchaseorderlineitem') - Part = apps.get_model('part', 'part') - - # Find all the StockItem objects without a purchase_price which point to a PurchaseOrder - items = StockItem.objects.filter(purchase_price=None).exclude(purchase_order=None) - - if items.count() > 0: # pragma: no cover - print(f"Found {items.count()} stock items with missing purchase price information") - - update_count = 0 - - for item in items: # pragma: no cover - - part_id = item.part - - po = item.purchase_order - - # Look for a matching PurchaseOrderLineItem (with a price) - lines = PurchaseOrderLineItem.objects.filter(part__part=part_id, order=po) - - if lines.exists(): - - for line in lines: - if getattr(line, 'purchase_price', None) is not None: - - # Copy pricing information across - item.purchase_price = line.purchase_price - item.purchases_price_currency = line.purchase_price_currency - - print(f"- Updating supplier price for {item.part.name} - {item.purchase_price} {item.purchase_price_currency}") - - update_count += 1 - - item.save() - - break - - if update_count > 0: # pragma: no cover - print(f"Updated pricing for {update_count} stock items") - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0063_auto_20210511_2343'), - ] - - operations = [ - migrations.RunPython(extract_purchase_price, reverse_code=migrations.RunPython.noop) - ] diff --git a/src/backend/InvenTree/stock/migrations/0065_auto_20210701_0509.py b/src/backend/InvenTree/stock/migrations/0065_auto_20210701_0509.py deleted file mode 100644 index f8718ac885..0000000000 --- a/src/backend/InvenTree/stock/migrations/0065_auto_20210701_0509.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 3.2.4 on 2021-07-01 05:09 - -import InvenTree.fields -from django.db import migrations -import djmoney.models.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0064_auto_20210621_1724'), - ] - - operations = [] - - xoperations = [ - migrations.AlterField( - model_name='stockitem', - name='purchase_price', - field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=4, default_currency='', help_text='Single unit purchase price at time of purchase', max_digits=19, null=True, verbose_name='Purchase Price'), - ), - migrations.AlterField( - model_name='stockitem', - name='purchase_price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0066_stockitem_scheduled_for_deletion.py b/src/backend/InvenTree/stock/migrations/0066_stockitem_scheduled_for_deletion.py deleted file mode 100644 index 45922d2f9f..0000000000 --- a/src/backend/InvenTree/stock/migrations/0066_stockitem_scheduled_for_deletion.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.4 on 2021-09-07 06:27 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0065_auto_20210701_0509'), - ] - - operations = [ - migrations.AddField( - model_name='stockitem', - name='scheduled_for_deletion', - field=models.BooleanField(default=False, help_text='This StockItem will be deleted by the background worker', verbose_name='Scheduled for deletion'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0067_alter_stockitem_part.py b/src/backend/InvenTree/stock/migrations/0067_alter_stockitem_part.py deleted file mode 100644 index 7f00b8f7b1..0000000000 --- a/src/backend/InvenTree/stock/migrations/0067_alter_stockitem_part.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.5 on 2021-11-04 12:40 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0074_partcategorystar'), - ('stock', '0066_stockitem_scheduled_for_deletion'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='part', - field=models.ForeignKey(help_text='Base part', limit_choices_to={'virtual': False}, on_delete=django.db.models.deletion.CASCADE, related_name='stock_items', to='part.part', verbose_name='Base Part'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0068_stockitem_serial_int.py b/src/backend/InvenTree/stock/migrations/0068_stockitem_serial_int.py deleted file mode 100644 index 874978dc61..0000000000 --- a/src/backend/InvenTree/stock/migrations/0068_stockitem_serial_int.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.5 on 2021-11-09 23:30 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0067_alter_stockitem_part'), - ] - - operations = [ - migrations.AddField( - model_name='stockitem', - name='serial_int', - field=models.IntegerField(default=0), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0069_auto_20211109_2347.py b/src/backend/InvenTree/stock/migrations/0069_auto_20211109_2347.py deleted file mode 100644 index 7399bc7963..0000000000 --- a/src/backend/InvenTree/stock/migrations/0069_auto_20211109_2347.py +++ /dev/null @@ -1,50 +0,0 @@ -# Generated by Django 3.2.5 on 2021-11-09 23:47 - -import re - -from django.db import migrations - - -def update_serials(apps, schema_editor): - """ - Rebuild the integer serial number field for existing StockItem objects - """ - - StockItem = apps.get_model('stock', 'stockitem') - - for item in StockItem.objects.all(): # pragma: no cover - - if item.serial is None: - # Skip items without existing serial numbers - continue - - serial = 0 - - result = re.match(r"^(\d+)", str(item.serial)) - - if result and len(result.groups()) == 1: - try: - serial = int(result.groups()[0]) - except Exception: - serial = 0 - - # Ensure the integer value is not too large for the database field - if serial > 0x7fffffff: - serial = 0x7fffffff - - item.serial_int = serial - item.save() - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0068_stockitem_serial_int'), - ] - - operations = [ - migrations.RunPython( - update_serials, - reverse_code=migrations.RunPython.noop, - ) - ] diff --git a/src/backend/InvenTree/stock/migrations/0070_auto_20211128_0151.py b/src/backend/InvenTree/stock/migrations/0070_auto_20211128_0151.py deleted file mode 100644 index 3c23c28f65..0000000000 --- a/src/backend/InvenTree/stock/migrations/0070_auto_20211128_0151.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 3.2.5 on 2021-11-28 01:51 - -import InvenTree.fields -import InvenTree.models -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0069_auto_20211109_2347'), - ] - - operations = [ - migrations.AddField( - model_name='stockitemattachment', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', null=True, verbose_name='Link'), - ), - migrations.AlterField( - model_name='stockitemattachment', - name='attachment', - field=models.FileField(blank=True, help_text='Select file to attach', null=True, upload_to='attachments', verbose_name='Attachment'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0071_auto_20211205_1733.py b/src/backend/InvenTree/stock/migrations/0071_auto_20211205_1733.py deleted file mode 100644 index 31cc5f1cc1..0000000000 --- a/src/backend/InvenTree/stock/migrations/0071_auto_20211205_1733.py +++ /dev/null @@ -1,50 +0,0 @@ -# Generated by Django 3.2.5 on 2021-12-05 06:33 - -from django.db import migrations - -import logging - - -logger = logging.getLogger('inventree') - - -def delete_scheduled(apps, schema_editor): - """ - Delete all stock items which are marked as 'scheduled_for_deletion'. - - The issue that this field was addressing has now been fixed, - and so we can all move on with our lives... - """ - - StockItem = apps.get_model('stock', 'stockitem') - - items = StockItem.objects.filter(scheduled_for_deletion=True) - - if items.count() > 0: - logger.info(f"Removing {items.count()} stock items scheduled for deletion") - - # Ensure any parent / child relationships are updated! - for item in items: - children = StockItem.objects.filter(parent=item) - children.update(parent=item.parent) - - item.delete() - - Task = apps.get_model('django_q', 'schedule') - - Task.objects.filter(func='stock.tasks.delete_old_stock_items').delete() - - -class Migration(migrations.Migration): - - dependencies = [ - ('django_q', '0007_ormq'), - ('stock', '0070_auto_20211128_0151'), - ] - - operations = [ - migrations.RunPython( - delete_scheduled, - reverse_code=migrations.RunPython.noop, - ) - ] diff --git a/src/backend/InvenTree/stock/migrations/0072_remove_stockitem_scheduled_for_deletion.py b/src/backend/InvenTree/stock/migrations/0072_remove_stockitem_scheduled_for_deletion.py deleted file mode 100644 index 0db2141299..0000000000 --- a/src/backend/InvenTree/stock/migrations/0072_remove_stockitem_scheduled_for_deletion.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.2.5 on 2021-12-05 06:49 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0071_auto_20211205_1733'), - ] - - operations = [ - migrations.RemoveField( - model_name='stockitem', - name='scheduled_for_deletion', - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0073_alter_stockitem_belongs_to.py b/src/backend/InvenTree/stock/migrations/0073_alter_stockitem_belongs_to.py deleted file mode 100644 index 93000ef0f8..0000000000 --- a/src/backend/InvenTree/stock/migrations/0073_alter_stockitem_belongs_to.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.10 on 2021-12-20 21:49 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0072_remove_stockitem_scheduled_for_deletion'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='belongs_to', - field=models.ForeignKey(blank=True, help_text='Is this item installed in another item?', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='installed_parts', to='stock.stockitem', verbose_name='Installed In'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0074_alter_stockitem_batch.py b/src/backend/InvenTree/stock/migrations/0074_alter_stockitem_batch.py deleted file mode 100644 index 5faa8e81b3..0000000000 --- a/src/backend/InvenTree/stock/migrations/0074_alter_stockitem_batch.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.12 on 2022-04-26 10:19 - -from django.db import migrations, models -import stock.generators -import stock.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0073_alter_stockitem_belongs_to'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='batch', - field=models.CharField(blank=True, default=stock.generators.generate_batch_code, help_text='Batch code for this stock item', max_length=100, null=True, verbose_name='Batch Code'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0075_auto_20220515_1440.py b/src/backend/InvenTree/stock/migrations/0075_auto_20220515_1440.py deleted file mode 100644 index 814a97edb3..0000000000 --- a/src/backend/InvenTree/stock/migrations/0075_auto_20220515_1440.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 3.2.13 on 2022-05-15 14:40 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0074_alter_stockitem_batch'), - ] - - operations = [ - migrations.AddField( - model_name='stockitem', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - migrations.AddField( - model_name='stocklocation', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - migrations.AlterUniqueTogether( - name='stocklocation', - unique_together=set(), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0076_alter_stockitem_status.py b/src/backend/InvenTree/stock/migrations/0076_alter_stockitem_status.py deleted file mode 100644 index 625fa372b6..0000000000 --- a/src/backend/InvenTree/stock/migrations/0076_alter_stockitem_status.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.13 on 2022-05-27 04:40 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0075_auto_20220515_1440'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='status', - field=models.PositiveIntegerField(choices=[(10, 'OK'), (50, 'Attention needed'), (55, 'Damaged'), (60, 'Destroyed'), (70, 'Lost'), (65, 'Rejected'), (75, 'Quarantined'), (85, 'Returned')], default=10, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0077_alter_stockitem_notes.py b/src/backend/InvenTree/stock/migrations/0077_alter_stockitem_notes.py deleted file mode 100644 index 5ea7834d71..0000000000 --- a/src/backend/InvenTree/stock/migrations/0077_alter_stockitem_notes.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.13 on 2022-06-20 07:28 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0076_alter_stockitem_status'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='notes', - field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Stock Item Notes', max_length=50000, null=True, verbose_name='Notes'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0078_alter_stockitem_supplier_part.py b/src/backend/InvenTree/stock/migrations/0078_alter_stockitem_supplier_part.py deleted file mode 100644 index 5af339e8aa..0000000000 --- a/src/backend/InvenTree/stock/migrations/0078_alter_stockitem_supplier_part.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.14 on 2022-07-15 07:49 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0046_alter_company_image'), - ('stock', '0077_alter_stockitem_notes'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='supplier_part', - field=models.ForeignKey(blank=True, help_text='Select a matching supplier part for this stock item', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stock_items', to='company.supplierpart', verbose_name='Supplier Part'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0079_alter_stocklocation_name.py b/src/backend/InvenTree/stock/migrations/0079_alter_stocklocation_name.py deleted file mode 100644 index 70f930b21c..0000000000 --- a/src/backend/InvenTree/stock/migrations/0079_alter_stocklocation_name.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.14 on 2022-07-20 16:09 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0078_alter_stockitem_supplier_part'), - ] - - operations = [ - migrations.AlterField( - model_name='stocklocation', - name='name', - field=models.CharField(help_text='Name', max_length=100, verbose_name='Name'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0080_stocklocation_pathstring.py b/src/backend/InvenTree/stock/migrations/0080_stocklocation_pathstring.py deleted file mode 100644 index 5a2de8595b..0000000000 --- a/src/backend/InvenTree/stock/migrations/0080_stocklocation_pathstring.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.14 on 2022-07-31 23:54 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0079_alter_stocklocation_name'), - ] - - operations = [ - migrations.AddField( - model_name='stocklocation', - name='pathstring', - field=models.CharField(blank=True, help_text='Path', max_length=250, verbose_name='Path'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0081_auto_20220801_0044.py b/src/backend/InvenTree/stock/migrations/0081_auto_20220801_0044.py deleted file mode 100644 index b755deb393..0000000000 --- a/src/backend/InvenTree/stock/migrations/0081_auto_20220801_0044.py +++ /dev/null @@ -1,48 +0,0 @@ -# Generated by Django 3.2.14 on 2022-08-01 00:44 - -from django.db import migrations - -from InvenTree.helpers import constructPathString - - -def update_pathstring(apps, schema_editor): - """Construct pathstring for all existing StockLocation objects""" - - StockLocation = apps.get_model('stock', 'stocklocation') - - n = StockLocation.objects.count() - - if n > 0: - - for loc in StockLocation.objects.all(): - - # Construct complete path for category - path = [loc.name] - - parent = loc.parent - - # Iterate up the tree - while parent is not None: - path = [parent.name] + path - parent = parent.parent - - pathstring = constructPathString(path) - - loc.pathstring = pathstring - loc.save() - - print(f"\n--- Updated 'pathstring' for {n} StockLocation objects ---\n") - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0080_stocklocation_pathstring'), - ] - - operations = [ - migrations.RunPython( - update_pathstring, - reverse_code=migrations.RunPython.noop - ) - ] diff --git a/src/backend/InvenTree/stock/migrations/0082_alter_stockitem_link.py b/src/backend/InvenTree/stock/migrations/0082_alter_stockitem_link.py deleted file mode 100644 index a7d0821859..0000000000 --- a/src/backend/InvenTree/stock/migrations/0082_alter_stockitem_link.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.15 on 2022-08-07 02:38 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0081_auto_20220801_0044'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='link', - field=InvenTree.fields.InvenTreeURLField(blank=True, help_text='Link to external URL', max_length=200, verbose_name='External Link'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0083_stocklocation_icon.py b/src/backend/InvenTree/stock/migrations/0083_stocklocation_icon.py deleted file mode 100644 index 7b19658157..0000000000 --- a/src/backend/InvenTree/stock/migrations/0083_stocklocation_icon.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.15 on 2022-08-15 08:38 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0082_alter_stockitem_link'), - ] - - operations = [ - migrations.AddField( - model_name='stocklocation', - name='icon', - field=models.CharField(blank=True, help_text='Icon (optional)', max_length=100, verbose_name='Icon'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0084_auto_20220903_0154.py b/src/backend/InvenTree/stock/migrations/0084_auto_20220903_0154.py deleted file mode 100644 index 88a3500b4c..0000000000 --- a/src/backend/InvenTree/stock/migrations/0084_auto_20220903_0154.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.15 on 2022-09-03 01:54 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0083_stocklocation_icon'), - ] - - operations = [ - migrations.AddField( - model_name='stockitem', - name='barcode_data', - field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), - ), - migrations.AddField( - model_name='stockitem', - name='barcode_hash', - field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0085_auto_20220903_0225.py b/src/backend/InvenTree/stock/migrations/0085_auto_20220903_0225.py deleted file mode 100644 index 75dc0fb0b9..0000000000 --- a/src/backend/InvenTree/stock/migrations/0085_auto_20220903_0225.py +++ /dev/null @@ -1,48 +0,0 @@ -# Generated by Django 3.2.15 on 2022-09-03 02:25 - -from django.db import migrations - - -def uid_to_barcode(apps, schama_editor): - """Migrate old 'uid' field to new 'barcode_hash' field""" - - StockItem = apps.get_model('stock', 'stockitem') - - # Find all StockItem objects with non-empty UID field - items = StockItem.objects.exclude(uid=None).exclude(uid='') - - for item in items: - item.barcode_hash = item.uid - item.save() - - if items.count() > 0: - print(f"Updated barcode data for {items.count()} StockItem objects") - -def barcode_to_uid(apps, schema_editor): - """Migrate new 'barcode_hash' field to old 'uid' field""" - - StockItem = apps.get_model('stock', 'stockitem') - - # Find all StockItem objects with non-empty UID field - items = StockItem.objects.exclude(barcode_hash=None).exclude(barcode_hash='') - - for item in items: - item.uid = item.barcode_hash - item.save() - - if items.count() > 0: - print(f"Updated barcode data for {items.count()} StockItem objects") - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0084_auto_20220903_0154'), - ] - - operations = [ - migrations.RunPython( - uid_to_barcode, - reverse_code=barcode_to_uid - ) - ] diff --git a/src/backend/InvenTree/stock/migrations/0086_remove_stockitem_uid.py b/src/backend/InvenTree/stock/migrations/0086_remove_stockitem_uid.py deleted file mode 100644 index 916558fabe..0000000000 --- a/src/backend/InvenTree/stock/migrations/0086_remove_stockitem_uid.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.2.15 on 2022-09-03 02:54 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0085_auto_20220903_0225'), - ] - - operations = [ - migrations.RemoveField( - model_name='stockitem', - name='uid', - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0087_auto_20220912_2341.py b/src/backend/InvenTree/stock/migrations/0087_auto_20220912_2341.py deleted file mode 100644 index af811e071b..0000000000 --- a/src/backend/InvenTree/stock/migrations/0087_auto_20220912_2341.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.15 on 2022-09-12 23:41 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0086_remove_stockitem_uid'), - ] - - operations = [ - migrations.AddField( - model_name='stocklocation', - name='barcode_data', - field=models.CharField(blank=True, help_text='Third party barcode data', max_length=500, verbose_name='Barcode Data'), - ), - migrations.AddField( - model_name='stocklocation', - name='barcode_hash', - field=models.CharField(blank=True, help_text='Unique hash of barcode data', max_length=128, verbose_name='Barcode Hash'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0088_remove_stockitem_infinite.py b/src/backend/InvenTree/stock/migrations/0088_remove_stockitem_infinite.py deleted file mode 100644 index 65df560bf6..0000000000 --- a/src/backend/InvenTree/stock/migrations/0088_remove_stockitem_infinite.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.2.15 on 2022-09-22 02:23 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0087_auto_20220912_2341'), - ] - - operations = [ - migrations.RemoveField( - model_name='stockitem', - name='infinite', - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0089_alter_stockitem_purchase_price.py b/src/backend/InvenTree/stock/migrations/0089_alter_stockitem_purchase_price.py deleted file mode 100644 index d1fe79c6bd..0000000000 --- a/src/backend/InvenTree/stock/migrations/0089_alter_stockitem_purchase_price.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.16 on 2022-11-11 01:53 - -import InvenTree.fields -from django.db import migrations -import djmoney.models.validators - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0088_remove_stockitem_infinite'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='purchase_price', - field=InvenTree.fields.InvenTreeModelMoneyField(blank=True, currency_choices=[], decimal_places=6, default_currency='', help_text='Single unit purchase price at time of purchase', max_digits=19, null=True, validators=[djmoney.models.validators.MinMoneyValidator(0)], verbose_name='Purchase Price'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0090_stocklocation_structural.py b/src/backend/InvenTree/stock/migrations/0090_stocklocation_structural.py deleted file mode 100644 index eca4617455..0000000000 --- a/src/backend/InvenTree/stock/migrations/0090_stocklocation_structural.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.16 on 2022-11-18 15:49 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0089_alter_stockitem_purchase_price'), - ] - - operations = [ - migrations.AddField( - model_name='stocklocation', - name='structural', - field=models.BooleanField(default=False, help_text="Stock items may not be directly located into a structural stock locations, but may be located to child locations.", verbose_name='Structural'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0091_alter_stockitem_delete_on_deplete.py b/src/backend/InvenTree/stock/migrations/0091_alter_stockitem_delete_on_deplete.py deleted file mode 100644 index 18ea2c0cd7..0000000000 --- a/src/backend/InvenTree/stock/migrations/0091_alter_stockitem_delete_on_deplete.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.16 on 2022-12-24 12:29 - -from django.db import migrations, models -import stock.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0090_stocklocation_structural'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='delete_on_deplete', - field=models.BooleanField(default=stock.models.default_delete_on_deplete, help_text='Delete this Stock Item when stock is depleted', verbose_name='Delete on deplete'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0092_alter_stockitem_updated.py b/src/backend/InvenTree/stock/migrations/0092_alter_stockitem_updated.py deleted file mode 100644 index 9252b12f62..0000000000 --- a/src/backend/InvenTree/stock/migrations/0092_alter_stockitem_updated.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.16 on 2023-01-15 14:04 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0091_alter_stockitem_delete_on_deplete'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='updated', - field=models.DateTimeField(auto_now=True, help_text='Timestamp of last update', null=True, verbose_name='Updated'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0093_auto_20230217_2140.py b/src/backend/InvenTree/stock/migrations/0093_auto_20230217_2140.py deleted file mode 100644 index 5629f8b4ee..0000000000 --- a/src/backend/InvenTree/stock/migrations/0093_auto_20230217_2140.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 3.2.16 on 2023-02-17 21:40 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0092_alter_stockitem_updated'), - ] - - operations = [ - migrations.AlterModelOptions( - name='stocklocation', - options={'verbose_name': 'Stock Location', 'verbose_name_plural': 'Stock Locations'}, - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0094_auto_20230220_0025.py b/src/backend/InvenTree/stock/migrations/0094_auto_20230220_0025.py deleted file mode 100644 index d0e515bfb4..0000000000 --- a/src/backend/InvenTree/stock/migrations/0094_auto_20230220_0025.py +++ /dev/null @@ -1,81 +0,0 @@ -# Generated by Django 3.2.18 on 2023-02-20 00:25 - -import logging - -from django.core.exceptions import FieldError -from django.db import migrations - -logger = logging.getLogger('inventree') - - -def fix_purchase_price(apps, schema_editor): - """Data migration for fixing historical issue with StockItem.purchase_price field. - - Ref: https://github.com/inventree/InvenTree/pull/4373 - - Due to an existing bug, if a PurchaseOrderLineItem was received, - which had: - - a) A SupplierPart with a non-unity pack size - b) A defined purchase_price - - then the StockItem.purchase_price was not calculated correctly! - - Specifically, the purchase_price was not divided through by the pack_size attribute. - - This migration fixes this by looking through all stock items which: - - - Is linked to a purchase order - - Have a purchase_price field - - Are linked to a supplier_part - - We can determine correctly that the calculation was misapplied - """ - - StockItem = apps.get_model('stock', 'stockitem') - - items = StockItem.objects.exclude( - purchase_order=None - ).exclude( - supplier_part=None - ).exclude( - purchase_price=None - ) - - try: - items = items.exclude(supplier_part__pack_size=1) - except FieldError: - pass - - n_updated = 0 - - for item in items: - # Grab a reference to the associated PurchaseOrder - # Trying to find an absolute match between this StockItem and an associated PurchaseOrderLineItem - po = item.purchase_order - for line in po.lines.all(): - # SupplierPart match - if line.part == item.supplier_part: - # Unit price matches original PurchaseOrder (and is thus incorrect) - if item.purchase_price == line.purchase_price: - item.purchase_price /= item.supplier_part.pack_size - item.save() - - n_updated += 1 - - if n_updated > 0: - logger.info(f"Corrected purchase_price field for {n_updated} stock items.") - - -class Migration(migrations.Migration): - - dependencies = [ - ('company', '0047_supplierpart_pack_size'), - ('stock', '0093_auto_20230217_2140'), - ] - - operations = [ - migrations.RunPython( - fix_purchase_price, - reverse_code=migrations.RunPython.noop, - ) - ] diff --git a/src/backend/InvenTree/stock/migrations/0095_stocklocation_external.py b/src/backend/InvenTree/stock/migrations/0095_stocklocation_external.py deleted file mode 100644 index 3163f4aa17..0000000000 --- a/src/backend/InvenTree/stock/migrations/0095_stocklocation_external.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.18 on 2023-02-20 12:22 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0094_auto_20230220_0025'), - ] - - operations = [ - migrations.AddField( - model_name='stocklocation', - name='external', - field=models.BooleanField(default=False, help_text='This is an external stock location', verbose_name='External'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0096_auto_20230330_1121.py b/src/backend/InvenTree/stock/migrations/0096_auto_20230330_1121.py deleted file mode 100644 index e8db1c5f77..0000000000 --- a/src/backend/InvenTree/stock/migrations/0096_auto_20230330_1121.py +++ /dev/null @@ -1,65 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-30 11:21 - -from django.db import migrations - - -def update_stock_history(apps, schema_editor): - """Data migration to fix a 'shortcoming' in the implementation of StockTracking history - - Prior to https://github.com/inventree/InvenTree/pull/4488, - shipping items via a SalesOrder did not record the SalesOrder in the tracking history. - This PR looks to add in SalesOrder history where it does not already exist: - - - Look for StockItems which are currently assigned to a SalesOrder - - Check that it does *not* have any appropriate history - - Add the appropriate history! - """ - - from stock.status_codes import StockHistoryCode - - StockItem = apps.get_model('stock', 'stockitem') - StockItemTracking = apps.get_model('stock', 'stockitemtracking') - - # Find StockItems which are marked as against a SalesOrder - items = StockItem.objects.exclude(sales_order=None) - - n = 0 - - for item in items: - # Find newest relevant history - history = StockItemTracking.objects.filter( - item=item, - tracking_type__in=[StockHistoryCode.SENT_TO_CUSTOMER, StockHistoryCode.SHIPPED_AGAINST_SALES_ORDER] - ).order_by('-date').first() - - if not history: - continue - - # We've already updated this one, it appears - if history.tracking_type != StockHistoryCode.SENT_TO_CUSTOMER: - continue - - # Update the 'deltas' of this history to include SalesOrder information - history.deltas['salesorder'] = item.sales_order.pk - - # Change the history type - history.tracking_type = StockHistoryCode.SHIPPED_AGAINST_SALES_ORDER.value - - history.save() - n += 1 - - if n > 0: - print(f"Updated {n} StockItemTracking entries with SalesOrder data") - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0095_stocklocation_external'), - ] - - operations = [ - migrations.RunPython( - update_stock_history, reverse_code=migrations.RunPython.noop, - ) - ] diff --git a/src/backend/InvenTree/stock/migrations/0097_alter_stockitem_notes.py b/src/backend/InvenTree/stock/migrations/0097_alter_stockitem_notes.py deleted file mode 100644 index d4bdbc6d5f..0000000000 --- a/src/backend/InvenTree/stock/migrations/0097_alter_stockitem_notes.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-19 00:37 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0096_auto_20230330_1121'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='notes', - field=InvenTree.fields.InvenTreeNotesField(blank=True, help_text='Markdown notes (optional)', max_length=50000, null=True, verbose_name='Notes'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0098_auto_20230427_2033.py b/src/backend/InvenTree/stock/migrations/0098_auto_20230427_2033.py deleted file mode 100644 index 097190b6d9..0000000000 --- a/src/backend/InvenTree/stock/migrations/0098_auto_20230427_2033.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-27 20:33 - -from django.db import migrations -import taggit.managers - - -class Migration(migrations.Migration): - - dependencies = [ - ('taggit', '0005_auto_20220424_2025'), - ('stock', '0097_alter_stockitem_notes'), - ] - - operations = [ - migrations.AddField( - model_name='stockitem', - name='tags', - field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), - ), - migrations.AddField( - model_name='stocklocation', - name='tags', - field=taggit.managers.TaggableManager(help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0099_alter_stockitem_status.py b/src/backend/InvenTree/stock/migrations/0099_alter_stockitem_status.py deleted file mode 100644 index 4765e52fe7..0000000000 --- a/src/backend/InvenTree/stock/migrations/0099_alter_stockitem_status.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.18 on 2023-05-13 05:15 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0098_auto_20230427_2033'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='status', - field=models.PositiveIntegerField(choices=[(10, 'OK'), (50, 'Attention needed'), (55, 'Damaged'), (60, 'Destroyed'), (70, 'Lost'), (65, 'Rejected'), (75, 'Quarantined')], default=10, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0100_auto_20230515_0004.py b/src/backend/InvenTree/stock/migrations/0100_auto_20230515_0004.py deleted file mode 100644 index ed5b923979..0000000000 --- a/src/backend/InvenTree/stock/migrations/0100_auto_20230515_0004.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-15 00:04 - -from django.db import migrations -import taggit.managers - - -class Migration(migrations.Migration): - - dependencies = [ - ('taggit', '0005_auto_20220424_2025'), - ('stock', '0099_alter_stockitem_status'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='tags', - field=taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), - ), - migrations.AlterField( - model_name='stocklocation', - name='tags', - field=taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0100_stockitem_consumed_by.py b/src/backend/InvenTree/stock/migrations/0100_stockitem_consumed_by.py deleted file mode 100644 index 6f302c3a75..0000000000 --- a/src/backend/InvenTree/stock/migrations/0100_stockitem_consumed_by.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-14 23:48 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0042_alter_build_notes'), - ('stock', '0100_auto_20230515_0004'), - ] - - operations = [ - migrations.AddField( - model_name='stockitem', - name='consumed_by', - field=models.ForeignKey(blank=True, help_text='Build order which consumed this stock item', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='consumed_stock', to='build.build', verbose_name='Consumed By'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0101_stockitemtestresult_metadata.py b/src/backend/InvenTree/stock/migrations/0101_stockitemtestresult_metadata.py deleted file mode 100644 index 95a6583f3f..0000000000 --- a/src/backend/InvenTree/stock/migrations/0101_stockitemtestresult_metadata.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.19 on 2023-05-25 16:06 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0100_stockitem_consumed_by'), - ] - - operations = [ - migrations.AddField( - model_name='stockitemtestresult', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0102_alter_stockitem_status.py b/src/backend/InvenTree/stock/migrations/0102_alter_stockitem_status.py deleted file mode 100644 index 90807a8c10..0000000000 --- a/src/backend/InvenTree/stock/migrations/0102_alter_stockitem_status.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.19 on 2023-06-04 17:43 - -import django.core.validators -from django.db import migrations, models -import stock.status_codes - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0101_stockitemtestresult_metadata'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='status', - field=models.PositiveIntegerField(choices=stock.status_codes.StockStatus.items(), default=10, validators=[django.core.validators.MinValueValidator(0)]), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0103_stock_location_types.py b/src/backend/InvenTree/stock/migrations/0103_stock_location_types.py deleted file mode 100644 index ce5ca5b78c..0000000000 --- a/src/backend/InvenTree/stock/migrations/0103_stock_location_types.py +++ /dev/null @@ -1,43 +0,0 @@ -# Generated by Django 3.2.20 on 2023-09-21 11:50 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0102_alter_stockitem_status'), - ] - - operations = [ - migrations.CreateModel( - name='StockLocationType', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('metadata', models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata')), - ('name', models.CharField(help_text='Name', max_length=100, verbose_name='Name')), - ('description', models.CharField(blank=True, help_text='Description (optional)', max_length=250, verbose_name='Description')), - ('icon', models.CharField(blank=True, help_text='Default icon for all locations that have no icon set (optional)', max_length=100, verbose_name='Icon')), - ], - options={ - 'verbose_name': 'Stock Location type', - 'verbose_name_plural': 'Stock Location types', - }, - ), - migrations.RenameField( - model_name='stocklocation', - old_name='icon', - new_name='custom_icon', - ), - migrations.AlterField( - model_name='stocklocation', - name='custom_icon', - field=models.CharField(blank=True, db_column='icon', help_text='Icon (optional)', max_length=100, verbose_name='Icon'), - ), - migrations.AddField( - model_name='stocklocation', - name='location_type', - field=models.ForeignKey(blank=True, help_text='Stock location type of this location', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stock_locations', to='stock.stocklocationtype', verbose_name='Location type'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0104_alter_stockitem_purchase_price_currency.py b/src/backend/InvenTree/stock/migrations/0104_alter_stockitem_purchase_price_currency.py deleted file mode 100644 index 6c25d34404..0000000000 --- a/src/backend/InvenTree/stock/migrations/0104_alter_stockitem_purchase_price_currency.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.22 on 2023-10-24 16:44 - -from django.db import migrations -import djmoney.models.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0103_stock_location_types'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='purchase_price_currency', - field=djmoney.models.fields.CurrencyField(choices=[], default='', editable=False, max_length=3, null=True), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0105_stockitemtestresult_template.py b/src/backend/InvenTree/stock/migrations/0105_stockitemtestresult_template.py deleted file mode 100644 index 79a2c11155..0000000000 --- a/src/backend/InvenTree/stock/migrations/0105_stockitemtestresult_template.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 4.2.9 on 2024-02-07 03:52 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0121_auto_20240207_0344'), - ('stock', '0104_alter_stockitem_purchase_price_currency'), - ] - - operations = [ - migrations.AddField( - model_name='stockitemtestresult', - name='template', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='test_results', to='part.parttesttemplate'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0106_auto_20240207_0353.py b/src/backend/InvenTree/stock/migrations/0106_auto_20240207_0353.py deleted file mode 100644 index c081c68f62..0000000000 --- a/src/backend/InvenTree/stock/migrations/0106_auto_20240207_0353.py +++ /dev/null @@ -1,133 +0,0 @@ -# Generated by Django 4.2.9 on 2024-02-07 03:53 - -from django.db import migrations - - -def set_template(apps, schema_editor): - """Matching existing StockItemTestResult objects to their associated template. - - - Use the 'key' value from the associated test object. - - Look at the referenced part first - - If no matches, look at parent part template(s) - - If still no matches, create a new PartTestTemplate object - """ - import time - import InvenTree.helpers - - StockItemTestResult = apps.get_model('stock', 'stockitemtestresult') - PartTestTemplate = apps.get_model('part', 'parttesttemplate') - Part = apps.get_model('part', 'part') - - # Look at any test results which do not match a template - results = StockItemTestResult.objects.filter(template=None) - - parts = results.values_list('stock_item__part', flat=True).distinct() - - n_results = results.count() - - if n_results == 0: - return - - print(f"\n{n_results} StockItemTestResult objects do not have matching templates!") - print(f"Updating test results for {len(parts)} unique parts...") - - # Keep a map of test templates - part_tree_map = {} - - t1 = time.time() - - new_templates = 0 - - # For each part with missing templates, work out what templates are missing - for pk in parts: - part = Part.objects.get(pk=pk) - tree_id = part.tree_id - # Find all results matching this part - part_results = results.filter(stock_item__part=part) - test_names = part_results.values_list('test', flat=True).distinct() - - key_map = part_tree_map.get(tree_id, None) or {} - - for name in test_names: - template = None - - key = InvenTree.helpers.generateTestKey(name) - - if template := key_map.get(key, None): - # We have a template for this key - pass - - elif template := PartTestTemplate.objects.filter(part__tree_id=part.tree_id, key=key).first(): - # We have found an existing template for this test - pass - - elif template := PartTestTemplate.objects.filter( - part__tree_id=part.tree_id, - part__lft__lte=part.lft, - part__rght__gte=part.rght, - key=key).first(): - # We have found an existing template for this test - pass - - # Create a new template, based on the available test information - else: - - # Find the parent part template - top_level_part = part - - while top_level_part.variant_of: - top_level_part = top_level_part.variant_of - - template = PartTestTemplate.objects.create( - part=top_level_part, - test_name=name, - key=key, - ) - - new_templates += 1 - - # Finally, update all matching results - part_results.filter(test=name).update(template=template) - - # Update the key map for this part tree - key_map[key] = template - - # Update the part tree map - part_tree_map[tree_id] = key_map - - t2 = time.time() - dt = t2 - t1 - - print(f"Updated {n_results} StockItemTestResult objects in {dt:.3f} seconds.") - - if new_templates > 0: - print(f"Created {new_templates} new templates!") - - # Check that there are now zero reamining results without templates - results = StockItemTestResult.objects.filter(template=None) - assert(results.count() == 0) - - -def remove_template(apps, schema_editor): - """Remove template links from existing StockItemTestResult objects.""" - - StockItemTestResult = apps.get_model('stock', 'stockitemtestresult') - results = StockItemTestResult.objects.all() - results.update(template=None) - - if results.count() > 0: - print(f"\nRemoved template links from {results.count()} StockItemTestResult objects") - - -class Migration(migrations.Migration): - - atomic = False - - dependencies = [ - ('stock', '0105_stockitemtestresult_template'), - ('part', '0121_auto_20240207_0344') - ] - - operations = [ - migrations.RunPython(set_template, reverse_code=remove_template), - ] diff --git a/src/backend/InvenTree/stock/migrations/0107_remove_stockitemtestresult_test_and_more.py b/src/backend/InvenTree/stock/migrations/0107_remove_stockitemtestresult_test_and_more.py deleted file mode 100644 index 7003f08870..0000000000 --- a/src/backend/InvenTree/stock/migrations/0107_remove_stockitemtestresult_test_and_more.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 4.2.9 on 2024-02-07 09:01 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0121_auto_20240207_0344'), - ('stock', '0106_auto_20240207_0353'), - ] - - operations = [ - migrations.RemoveField( - model_name='stockitemtestresult', - name='test', - ), - migrations.AlterField( - model_name='stockitemtestresult', - name='template', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='test_results', to='part.parttesttemplate'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0108_auto_20240219_0252.py b/src/backend/InvenTree/stock/migrations/0108_auto_20240219_0252.py deleted file mode 100644 index 185eeb0f2a..0000000000 --- a/src/backend/InvenTree/stock/migrations/0108_auto_20240219_0252.py +++ /dev/null @@ -1,114 +0,0 @@ -# Generated by Django 4.2.10 on 2024-02-19 02:52 - -from django.db import migrations -from django.db.models import F, OuterRef, Subquery, IntegerField - - -def update_templates(apps, schema_editor): - """Run data migration to fix potentially mis-applied data migration. - - Ref: https://github.com/inventree/InvenTree/pull/6514 - - The previous data migration (stock.0106_auto_20240207_0353) had a bug, - where it would look for any matching PartTestTemplate objects for a given StockItemTestResult, - as long as the "part tree ID" was the same. - - However, if the template was defined for a part on a different *branch* of the tree, - the wrong template could be applied. - - This is really only the case where the user has a very complex set of nested part variants, - but still there is a potential for a mis-match. - - This data migration will attempt to fix any mis-applied templates. - """ - - PartTestTemplate = apps.get_model('part', 'PartTestTemplate') - StockItemTestResult = apps.get_model('stock', 'StockItemTestResult') - - # Find any StockItemTestResult objects which match a "bad" template - # Here a "bad" template points to a Part which is not *above* the part in the tree - bad_results = StockItemTestResult.objects.exclude( - stock_item__part__tree_id=F('template__part__tree_id'), - stock_item__part__lft__gte=F('template__part__lft'), - stock_item__part__rght__lte=F('template__part__rght'), - ) - - n = bad_results.count() - - if n == 0: - # Escape early - no bad results! - return - - print(f"Found {n} StockItemTestResult objects with bad templates...") - - # For each bad result, attempt to find a matching template - # Here, a matching template must point to a part *above* the part in the tree - # Annotate the queryset with a "matching template" - - template_query = PartTestTemplate.objects.filter( - part__tree_id=OuterRef('stock_item__part__tree_id'), - part__lft__lte=OuterRef('stock_item__part__lft'), - part__rght__gte=OuterRef('stock_item__part__rght'), - key=OuterRef('template__key') - ).order_by('part__level').values('pk') - - bad_results = bad_results.annotate( - matching_template=Subquery(template_query[:1], output_field=IntegerField()) - ) - - # Update the results for which we have a "good" matching template - matching_results = bad_results.filter(matching_template__isnull=False) - missing_results = bad_results.filter(matching_template__isnull=True) - - results_to_update = [] - - for result in matching_results: - if result.template.pk != result.matching_template: - result.template = PartTestTemplate.objects.get(pk=result.matching_template) - results_to_update.append(result) - - if len(results_to_update) > 0: - # Update any results which point to the wrong template, but have a matching template - print("Updating", len(results_to_update), "matching templates...") - StockItemTestResult.objects.bulk_update(results_to_update, ['template']) - - results_to_update = [] - - # For the remaining results, we need to create a new template - for result in missing_results: - # Check that a template does *not* exist already - if template := PartTestTemplate.objects.filter( - part__tree_id=result.stock_item.part.tree_id, - part__lft__lte=result.stock_item.part.lft, - part__rght__gte=result.stock_item.part.rght, - key=result.template.key - ).first(): - pass - else: - # Create a new template (by copying the old one) - template = result.template - template.part = result.stock_item.part - template.pk = None - template.save() - template.refresh_from_db() - - result.template = template - results_to_update.append(result) - - if len(results_to_update) > 0: - print("Updating", len(results_to_update), "missing templates...") - StockItemTestResult.objects.bulk_update(results_to_update, ['template']) - - # Finall, check that there are no longer any "bad" results - assert(bad_results.order_by('pk').count() == 0) - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0107_remove_stockitemtestresult_test_and_more'), - ] - - operations = [ - migrations.RunPython(update_templates, reverse_code=migrations.RunPython.noop) - ] diff --git a/src/backend/InvenTree/stock/migrations/0109_add_additional_test_fields.py b/src/backend/InvenTree/stock/migrations/0109_add_additional_test_fields.py deleted file mode 100644 index 002bb3b139..0000000000 --- a/src/backend/InvenTree/stock/migrations/0109_add_additional_test_fields.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 3.2.23 on 2023-12-18 18:52 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0108_auto_20240219_0252'), - ] - - operations = [ - migrations.AddField( - model_name='stockitemtestresult', - name='finished_datetime', - field=models.DateTimeField(blank=True, help_text='The timestamp of the test finish', null=True, verbose_name='Finished'), - ), - migrations.AddField( - model_name='stockitemtestresult', - name='started_datetime', - field=models.DateTimeField(blank=True, help_text='The timestamp of the test start', null=True, verbose_name='Started'), - ), - migrations.AddField( - model_name='stockitemtestresult', - name='test_station', - field=models.CharField(blank=True, help_text='The identifier of the test station where the test was performed', max_length=500, verbose_name='Test station'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0110_alter_stockitemtestresult_finished_datetime_and_more.py b/src/backend/InvenTree/stock/migrations/0110_alter_stockitemtestresult_finished_datetime_and_more.py deleted file mode 100644 index 5ded82c134..0000000000 --- a/src/backend/InvenTree/stock/migrations/0110_alter_stockitemtestresult_finished_datetime_and_more.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 4.2.11 on 2024-03-27 04:25 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0109_add_additional_test_fields'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitemtestresult', - name='finished_datetime', - field=models.DateTimeField(blank=True, help_text='The timestamp of the test finish', null=True, verbose_name='Finished'), - ), - migrations.AlterField( - model_name='stockitemtestresult', - name='started_datetime', - field=models.DateTimeField(blank=True, help_text='The timestamp of the test start', null=True, verbose_name='Started'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0111_delete_stockitemattachment.py b/src/backend/InvenTree/stock/migrations/0111_delete_stockitemattachment.py deleted file mode 100644 index 1526a63799..0000000000 --- a/src/backend/InvenTree/stock/migrations/0111_delete_stockitemattachment.py +++ /dev/null @@ -1,21 +0,0 @@ -# Generated by Django 4.2.12 on 2024-06-09 09:02 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('build', '0050_auto_20240508_0138'), - ('common', '0026_auto_20240608_1238'), - ('company', '0069_company_active'), - ('order', '0099_alter_salesorder_status'), - ('part', '0123_parttesttemplate_choices'), - ('stock', '0110_alter_stockitemtestresult_finished_datetime_and_more') - ] - - operations = [ - migrations.DeleteModel( - name='StockItemAttachment', - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0112_alter_stocklocation_custom_icon_and_more.py b/src/backend/InvenTree/stock/migrations/0112_alter_stocklocation_custom_icon_and_more.py deleted file mode 100644 index 21b48b91da..0000000000 --- a/src/backend/InvenTree/stock/migrations/0112_alter_stocklocation_custom_icon_and_more.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 4.2.11 on 2024-07-20 22:30 - -import common.icons -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0111_delete_stockitemattachment'), - ] - - operations = [ - migrations.AlterField( - model_name='stocklocation', - name='custom_icon', - field=models.CharField(blank=True, db_column='icon', help_text='Icon (optional)', max_length=100, validators=[common.icons.validate_icon], verbose_name='Icon'), - ), - migrations.AlterField( - model_name='stocklocationtype', - name='icon', - field=models.CharField(blank=True, help_text='Default icon for all locations that have no icon set (optional)', max_length=100, validators=[common.icons.validate_icon], verbose_name='Icon'), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0113_stockitem_status_custom_key_and_more.py b/src/backend/InvenTree/stock/migrations/0113_stockitem_status_custom_key_and_more.py deleted file mode 100644 index 1b8bbf1304..0000000000 --- a/src/backend/InvenTree/stock/migrations/0113_stockitem_status_custom_key_and_more.py +++ /dev/null @@ -1,49 +0,0 @@ -# Generated by Django 4.2.14 on 2024-08-07 22:40 - -import django.core.validators -from django.db import migrations - -import generic.states -import generic.states.fields -import generic.states.validators -import InvenTree.status_codes - - -class Migration(migrations.Migration): - - dependencies = [ - ("stock", "0112_alter_stocklocation_custom_icon_and_more"), - ] - - operations = [ - migrations.AddField( - model_name="stockitem", - name="status_custom_key", - field=generic.states.fields.ExtraInvenTreeCustomStatusModelField( - blank=True, - default=None, - help_text="Additional status information for this item", - null=True, - verbose_name="Custom status key", - validators=[ - generic.states.validators.CustomStatusCodeValidator( - status_class=InvenTree.status_codes.StockStatus - ), - ], - ), - ), - migrations.AlterField( - model_name="stockitem", - name="status", - field=generic.states.fields.InvenTreeCustomStatusModelField( - choices=InvenTree.status_codes.StockStatus.items(), - default=10, - validators=[ - django.core.validators.MinValueValidator(0), - generic.states.validators.CustomStatusCodeValidator( - status_class=InvenTree.status_codes.StockStatus - ), - ], - ), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0114_alter_stocklocation_custom_icon.py b/src/backend/InvenTree/stock/migrations/0114_alter_stocklocation_custom_icon.py deleted file mode 100644 index 1e21e79e8a..0000000000 --- a/src/backend/InvenTree/stock/migrations/0114_alter_stocklocation_custom_icon.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 4.2.19 on 2025-02-25 22:14 - -import common.icons -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("stock", "0113_stockitem_status_custom_key_and_more"), - ] - - operations = [ - migrations.AlterField( - model_name="stocklocation", - name="custom_icon", - field=models.CharField( - blank=True, - db_column="icon", - help_text="Icon (optional)", - max_length=100, - null=True, - validators=[common.icons.validate_icon], - verbose_name="Icon", - ), - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0115_auto_20250221_1323.py b/src/backend/InvenTree/stock/migrations/0115_auto_20250221_1323.py deleted file mode 100644 index 0f232d3ec5..0000000000 --- a/src/backend/InvenTree/stock/migrations/0115_auto_20250221_1323.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 4.2.19 on 2025-02-21 13:23 - -import InvenTree.fields -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("stock", "0114_alter_stocklocation_custom_icon"), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='link', - field=models.TextField(null=True, blank=True) # Temporary change to force new ALTER COLUMN operation in the next migration - ), - ] diff --git a/src/backend/InvenTree/stock/migrations/0116_alter_stockitem_link.py b/src/backend/InvenTree/stock/migrations/0116_alter_stockitem_link.py deleted file mode 100644 index cd4d9038c6..0000000000 --- a/src/backend/InvenTree/stock/migrations/0116_alter_stockitem_link.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 4.2.19 on 2025-02-21 13:46 - -import InvenTree.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("stock", "0115_auto_20250221_1323"), - ] - - operations = [ - migrations.AlterField( - model_name="stockitem", - name="link", - field=InvenTree.fields.InvenTreeURLField( - blank=True, - help_text="Link to external URL", - max_length=2000, - verbose_name="External Link", - ), - ), - ] diff --git a/src/backend/InvenTree/stock/test_migrations.py b/src/backend/InvenTree/stock/test_migrations.py index 0a4bebb8ce..f3284edb04 100644 --- a/src/backend/InvenTree/stock/test_migrations.py +++ b/src/backend/InvenTree/stock/test_migrations.py @@ -3,420 +3,6 @@ from django_test_migrations.contrib.unittest_case import MigratorTestCase -class TestSerialNumberMigration(MigratorTestCase): - """Test data migration which updates serial numbers.""" - - migrate_from = ('stock', '0067_alter_stockitem_part') - migrate_to = ('stock', '0070_auto_20211128_0151') - - def prepare(self): - """Create initial data for this migration.""" - Part = self.old_state.apps.get_model('part', 'part') - StockItem = self.old_state.apps.get_model('stock', 'stockitem') - - # Create a base part - my_part = Part.objects.create( - name='PART-123', - description='Some part', - active=True, - trackable=True, - level=0, - tree_id=0, - lft=0, - rght=0, - ) - - # Create some serialized stock items - for sn in range(10, 20): - StockItem.objects.create( - part=my_part, quantity=1, serial=sn, level=0, tree_id=0, lft=0, rght=0 - ) - - # Create a stock item with a very large serial number - item = StockItem.objects.create( - part=my_part, - quantity=1, - serial='9999999999999999999999999999999999999999999999999999999999999', - level=0, - tree_id=0, - lft=0, - rght=0, - ) - - self.big_ref_pk = item.pk - - def test_migrations(self): - """Test that the migrations have been applied correctly.""" - StockItem = self.new_state.apps.get_model('stock', 'stockitem') - - # Check that the serial number integer conversion has been applied correctly - for sn in range(10, 20): - item = StockItem.objects.get(serial_int=sn) - - self.assertEqual(item.serial, str(sn)) - - big_ref_item = StockItem.objects.get(pk=self.big_ref_pk) - - # Check that the StockItem maximum serial number - self.assertEqual( - big_ref_item.serial, - '9999999999999999999999999999999999999999999999999999999999999', - ) - - self.assertEqual(big_ref_item.serial_int, 0x7FFFFFFF) - - -class TestScheduledForDeletionMigration(MigratorTestCase): - """Test data migration for removing 'scheduled_for_deletion' field.""" - - migrate_from = ('stock', '0066_stockitem_scheduled_for_deletion') - migrate_to = ('stock', '0073_alter_stockitem_belongs_to') - - def prepare(self): - """Create some initial stock items.""" - Part = self.old_state.apps.get_model('part', 'part') - StockItem = self.old_state.apps.get_model('stock', 'stockitem') - - for idx in range(5): - part = Part.objects.create( - name=f'Part_{idx}', - description='Just a part, nothing to see here', - active=True, - level=0, - tree_id=0, - lft=0, - rght=0, - ) - - for jj in range(5): - StockItem.objects.create( - part=part, - quantity=jj + 5, - level=0, - tree_id=0, - lft=0, - rght=0, - scheduled_for_deletion=True, - ) - - # For extra points, create some parent-child relationships between stock items - part = Part.objects.first() - - item_1 = StockItem.objects.create( - part=part, - quantity=100, - level=0, - tree_id=0, - lft=0, - rght=0, - scheduled_for_deletion=True, - ) - - for _ in range(3): - StockItem.objects.create( - part=part, - quantity=200, - level=0, - tree_id=0, - lft=0, - rght=0, - scheduled_for_deletion=False, - parent=item_1, - ) - - self.assertEqual(StockItem.objects.count(), 29) - - def test_migration(self): - """Test that all stock items were actually removed.""" - StockItem = self.new_state.apps.get_model('stock', 'stockitem') - - # All the "scheduled for deletion" items have been removed - self.assertEqual(StockItem.objects.count(), 3) - - -class TestTestResultMigration(MigratorTestCase): - """Unit tests for StockItemTestResult data migrations.""" - - migrate_from = ('stock', '0103_stock_location_types') - migrate_to = ('stock', '0107_remove_stockitemtestresult_test_and_more') - - test_keys = { - 'appliedpaint': 'Applied Paint', - 'programmed': 'Programmed', - 'checkedresultcode': 'Checked Result CODE', - } - - def prepare(self): - """Create initial data.""" - Part = self.old_state.apps.get_model('part', 'part') - PartTestTemplate = self.old_state.apps.get_model('part', 'parttesttemplate') - StockItem = self.old_state.apps.get_model('stock', 'stockitem') - StockItemTestResult = self.old_state.apps.get_model( - 'stock', 'stockitemtestresult' - ) - - # Create a test part - parent_part = Part.objects.create( - name='Parent Part', - description='A parent part', - is_template=True, - active=True, - trackable=True, - level=0, - tree_id=1, - lft=0, - rght=0, - ) - - # Create some child parts - children = [ - Part.objects.create( - name=f'Child part {idx}', - description='A child part', - variant_of=parent_part, - active=True, - trackable=True, - level=0, - tree_id=1, - lft=0, - rght=0, - ) - for idx in range(3) - ] - - # Create some stock items - for ii, child in enumerate(children): - for jj in range(4): - si = StockItem.objects.create( - part=child, - serial=str(1 + jj), - quantity=1, - tree_id=0, - level=0, - lft=0, - rght=0, - ) - - # Create some test results - for v in self.test_keys.values(): - StockItemTestResult.objects.create( - stock_item=si, test=v, result=True, value=f'Result: {ii} : {jj}' - ) - - # Check initial record counts - self.assertEqual(PartTestTemplate.objects.count(), 0) - self.assertEqual(StockItemTestResult.objects.count(), 36) - - def test_migration(self): - """Test that the migrations were applied as expected.""" - Part = self.new_state.apps.get_model('part', 'part') - PartTestTemplate = self.new_state.apps.get_model('part', 'parttesttemplate') - StockItem = self.new_state.apps.get_model('stock', 'stockitem') - StockItemTestResult = self.new_state.apps.get_model( - 'stock', 'stockitemtestresult' - ) - - # Test that original record counts are correct - self.assertEqual(Part.objects.count(), 4) - self.assertEqual(StockItem.objects.count(), 12) - self.assertEqual(StockItemTestResult.objects.count(), 36) - - # Two more test templates should have been created - self.assertEqual(PartTestTemplate.objects.count(), 3) - - for k in self.test_keys: - self.assertTrue(PartTestTemplate.objects.filter(key=k).exists()) - - for result in StockItemTestResult.objects.all(): - self.assertIsNotNone(result.template) - - -class TestPathstringMigration(MigratorTestCase): - """Unit tests for StockLocation.Pathstring data migrations.""" - - migrate_from = ('stock', '0080_stocklocation_pathstring') - migrate_to = ('stock', '0081_auto_20220801_0044') - - def prepare(self): - """Create initial data.""" - StockLocation = self.old_state.apps.get_model('stock', 'stocklocation') - - # Create a test StockLocation - loc1 = StockLocation.objects.create( - name='Loc 1', level=0, lft=0, rght=0, tree_id=0 - ) - loc2 = StockLocation.objects.create( - name='Loc 2', parent=loc1, level=1, lft=0, rght=0, tree_id=0 - ) - StockLocation.objects.create( - name='Loc 3', parent=loc2, level=2, lft=0, rght=0, tree_id=0 - ) - StockLocation.objects.create(name='Loc 4', level=0, lft=0, rght=0, tree_id=0) - - # Check initial record counts - self.assertEqual(StockLocation.objects.count(), 4) - - def test_migration(self): - """Test that the migrations were applied as expected.""" - StockLocation = self.old_state.apps.get_model('stock', 'stocklocation') - - self.assertEqual(StockLocation.objects.count(), 4) - test_data = { - 'Loc 1': 'Loc 1', - 'Loc 2': 'Loc 1/Loc 2', - 'Loc 3': 'Loc 1/Loc 2/Loc 3', - 'Loc 4': 'Loc 4', - } - for loc_name, pathstring in test_data.items(): - loc = StockLocation.objects.get(name=loc_name) - self.assertEqual(loc.pathstring, pathstring) - - -class TestBarcodeToUidMigration(MigratorTestCase): - """Unit tests for barcode to uid data migrations.""" - - migrate_from = ('stock', '0084_auto_20220903_0154') - migrate_to = ('stock', '0085_auto_20220903_0225') - - def prepare(self): - """Create initial data.""" - Part = self.old_state.apps.get_model('part', 'part') - StockItem = self.old_state.apps.get_model('stock', 'stockitem') - - # Create a test StockItem - part = Part.objects.create(name='test', level=0, lft=0, rght=0, tree_id=0) - StockItem.objects.create( - part_id=part.id, uid='12345', level=0, lft=0, rght=0, tree_id=0 - ) - self.assertEqual(StockItem.objects.count(), 1) - - def test_migration(self): - """Test that the migrations were applied as expected.""" - StockItem = self.new_state.apps.get_model('stock', 'StockItem') - - self.assertEqual(StockItem.objects.count(), 1) - item = StockItem.objects.first() - self.assertEqual(item.barcode_hash, '12345') - self.assertEqual(item.uid, '12345') - - -class TestBarcodeToUiReversedMigration(MigratorTestCase): - """Unit tests for barcode to uid data migrations.""" - - migrate_to = ('stock', '0084_auto_20220903_0154') - migrate_from = ('stock', '0085_auto_20220903_0225') - - def prepare(self): - """Create initial data.""" - Part = self.old_state.apps.get_model('part', 'part') - StockItem = self.old_state.apps.get_model('stock', 'stockitem') - - # Create a test StockItem - part = Part.objects.create(name='test', level=0, lft=0, rght=0, tree_id=0) - StockItem.objects.create( - part_id=part.id, barcode_hash='54321', level=0, lft=0, rght=0, tree_id=0 - ) - self.assertEqual(StockItem.objects.count(), 1) - - def test_migration(self): - """Test that the migrations were applied as expected.""" - StockItem = self.new_state.apps.get_model('stock', 'StockItem') - - self.assertEqual(StockItem.objects.count(), 1) - item = StockItem.objects.first() - self.assertEqual(item.barcode_hash, '54321') - self.assertEqual(item.uid, '54321') - - -class TestPartTestTemplateTreeFixMigration(MigratorTestCase): - """Unit tests for fixing issues with PartTestTemplate tree branch confusion migrations.""" - - migrate_from = ('stock', '0107_remove_stockitemtestresult_test_and_more') - migrate_to = ('stock', '0108_auto_20240219_0252') - - def prepare(self): - """Create initial data.""" - Part = self.old_state.apps.get_model('part', 'part') - PartTestTemplate = self.old_state.apps.get_model('part', 'PartTestTemplate') - StockItem = self.old_state.apps.get_model('stock', 'StockItem') - StockItemTestResult = self.old_state.apps.get_model( - 'stock', 'StockItemTestResult' - ) - - p = Part.objects.create(name='test', level=0, lft=0, rght=0, tree_id=0) - p2 = Part.objects.create(name='test 2', level=0, lft=0, rght=0, tree_id=4) - tmpl = PartTestTemplate.objects.create(part=p2, key='test_key') - stock = StockItem.objects.create(part=p, level=0, lft=3, rght=0, tree_id=0) - StockItemTestResult.objects.create(template=tmpl, stock_item=stock) - self.assertEqual(StockItemTestResult.objects.count(), 1) - self.assertEqual(PartTestTemplate.objects.count(), 1) - - def test_migration(self): - """Test that the migrations were applied as expected.""" - PartTestTemplate = self.old_state.apps.get_model('part', 'PartTestTemplate') - StockItemTestResult = self.old_state.apps.get_model( - 'stock', 'StockItemTestResult' - ) - - self.assertEqual(StockItemTestResult.objects.count(), 1) - self.assertEqual(PartTestTemplate.objects.count(), 2) - - -class TestStockItemTrackingMigration(MigratorTestCase): - """Unit tests for StockItemTracking code migrations.""" - - migrate_from = ('stock', '0095_stocklocation_external') - migrate_to = ('stock', '0096_auto_20230330_1121') - - def prepare(self): - """Create initial data.""" - from stock.status_codes import StockHistoryCode - - Part = self.old_state.apps.get_model('part', 'part') - SalesOrder = self.old_state.apps.get_model('order', 'salesorder') - StockItem = self.old_state.apps.get_model('stock', 'stockitem') - StockItemTracking = self.old_state.apps.get_model('stock', 'stockitemtracking') - - # Create a test StockItem - part = Part.objects.create(name='test', level=0, lft=0, rght=0, tree_id=0) - so = SalesOrder.objects.create(reference='123') - si = StockItem.objects.create( - part_id=part.id, sales_order=so, level=0, lft=0, rght=0, tree_id=0 - ) - si2 = StockItem.objects.create( - part_id=part.id, sales_order=so, level=0, lft=0, rght=0, tree_id=0 - ) - StockItem.objects.create( - part_id=part.id, sales_order=so, level=0, lft=0, rght=0, tree_id=0 - ) - - StockItemTracking.objects.create( - item_id=si.pk, - tracking_type=StockHistoryCode.SENT_TO_CUSTOMER.value, - deltas={'foo': 'bar'}, - ) - StockItemTracking.objects.create( - item_id=si2.pk, - tracking_type=StockHistoryCode.SHIPPED_AGAINST_SALES_ORDER.value, - deltas={'foo': 'bar'}, - ) - self.assertEqual(StockItemTracking.objects.count(), 2) - - def test_migration(self): - """Test that the migrations were applied as expected.""" - from stock.status_codes import StockHistoryCode - - StockItemTracking = self.old_state.apps.get_model('stock', 'stockitemtracking') - - self.assertEqual(StockItemTracking.objects.count(), 2) - item = StockItemTracking.objects.first() - self.assertEqual( - item.tracking_type, StockHistoryCode.SHIPPED_AGAINST_SALES_ORDER - ) - self.assertIn('salesorder', item.deltas) - self.assertEqual(item.deltas['salesorder'], 1) - - class TestCreationDateMigration(MigratorTestCase): """Test the backfill data migration for StockItem.creation_date (stock.0121). diff --git a/src/backend/InvenTree/users/migrations/0001_initial.py b/src/backend/InvenTree/users/migrations/0001_initial.py deleted file mode 100644 index 04071c5a63..0000000000 --- a/src/backend/InvenTree/users/migrations/0001_initial.py +++ /dev/null @@ -1,31 +0,0 @@ -# Generated by Django 3.0.7 on 2020-10-03 13:44 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('auth', '0011_update_proxy_permissions'), - ] - - operations = [ - migrations.CreateModel( - name='RuleSet', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(choices=[('general', 'General'), ('admin', 'Admin'), ('part', 'Parts'), ('stock', 'Stock'), ('build', 'Build Orders'), ('supplier', 'Suppliers'), ('purchase_order', 'Purchase Orders'), ('customer', 'Customers'), ('sales_order', 'Sales Orders')], help_text='Permission set', max_length=50)), - ('can_view', models.BooleanField(default=True, help_text='Permission to view items', verbose_name='View')), - ('can_add', models.BooleanField(default=False, help_text='Permission to add items', verbose_name='Create')), - ('can_change', models.BooleanField(default=False, help_text='Permissions to edit items', verbose_name='Update')), - ('can_delete', models.BooleanField(default=False, help_text='Permission to delete items', verbose_name='Delete')), - ('group', models.ForeignKey(help_text='Group', on_delete=django.db.models.deletion.CASCADE, related_name='rule_sets', to='auth.Group')), - ], - options={ - 'unique_together': {('name', 'group')}, - }, - ), - ] diff --git a/src/backend/InvenTree/users/migrations/0001_squashed_0015_alter_userprofile_type.py b/src/backend/InvenTree/users/migrations/0001_squashed_0015_alter_userprofile_type.py new file mode 100644 index 0000000000..5fbbc7601c --- /dev/null +++ b/src/backend/InvenTree/users/migrations/0001_squashed_0015_alter_userprofile_type.py @@ -0,0 +1,406 @@ +# Generated by Django 5.2.17 on 2026-09-08 04:54 + +from django.conf import settings +from django.db import migrations, models + +import django.core.validators +import django.db.models.deletion +import users.models +import users.ruleset + + +class Migration(migrations.Migration): + replaces = [ + ('users', '0001_initial'), + ('users', '0002_auto_20201004_0158'), + ('users', '0003_auto_20201005_2227'), + ('users', '0004_auto_20210113_1909'), + ('users', '0005_owner_model'), + ('users', '0006_alter_ruleset_name'), + ('users', '0007_alter_ruleset_name'), + ('users', '0008_apitoken'), + ('users', '0009_auto_20231020_2356'), + ('users', '0010_alter_apitoken_key'), + ('users', '0011_auto_20240523_1640'), + ('users', '0012_alter_ruleset_can_view'), + ('users', '0013_migrate_mfa_20240408_1659'), + ('users', '0014_userprofile'), + ('users', '0015_alter_userprofile_type'), + ] + + initial = True + + dependencies = [ + ('auth', '0011_update_proxy_permissions'), + ('auth', '0012_alter_user_first_name_max_length'), + ('contenttypes', '0002_remove_content_type_name'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Owner', + fields=[ + ( + 'id', + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name='ID', + ), + ), + ('owner_id', models.PositiveIntegerField(blank=True, null=True)), + ( + 'owner_type', + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to='contenttypes.contenttype', + ), + ), + ], + options={ + 'constraints': [ + models.UniqueConstraint( + fields=('owner_type', 'owner_id'), name='unique_owner' + ) + ] + }, + ), + migrations.CreateModel( + name='RuleSet', + fields=[ + ( + 'id', + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name='ID', + ), + ), + ( + 'name', + models.CharField( + choices=[ + (users.ruleset.RuleSetEnum['ADMIN'], 'Admin'), + ( + users.ruleset.RuleSetEnum['PART_CATEGORY'], + 'Part Categories', + ), + (users.ruleset.RuleSetEnum['PART'], 'Parts'), + (users.ruleset.RuleSetEnum['BOM'], 'Bills of Material'), + ( + users.ruleset.RuleSetEnum['STOCK_LOCATION'], + 'Stock Locations', + ), + (users.ruleset.RuleSetEnum['STOCK'], 'Stock Items'), + (users.ruleset.RuleSetEnum['BUILD'], 'Build Orders'), + ( + users.ruleset.RuleSetEnum['PURCHASE_ORDER'], + 'Purchase Orders', + ), + (users.ruleset.RuleSetEnum['SALES_ORDER'], 'Sales Orders'), + ( + users.ruleset.RuleSetEnum['RETURN_ORDER'], + 'Return Orders', + ), + ( + users.ruleset.RuleSetEnum['TRANSFER_ORDER'], + 'Transfer Orders', + ), + ], + help_text='Permission set', + max_length=50, + ), + ), + ( + 'can_view', + models.BooleanField( + default=False, + help_text='Permission to view items', + verbose_name='View', + ), + ), + ( + 'can_add', + models.BooleanField( + default=False, + help_text='Permission to add items', + verbose_name='Add', + ), + ), + ( + 'can_change', + models.BooleanField( + default=False, + help_text='Permissions to edit items', + verbose_name='Change', + ), + ), + ( + 'can_delete', + models.BooleanField( + default=False, + help_text='Permission to delete items', + verbose_name='Delete', + ), + ), + ( + 'group', + models.ForeignKey( + help_text='Group', + on_delete=django.db.models.deletion.CASCADE, + related_name='rule_sets', + to='auth.group', + ), + ), + ], + options={'unique_together': {('name', 'group')}}, + ), + migrations.CreateModel( + name='ApiToken', + fields=[ + ( + 'id', + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name='ID', + ), + ), + ( + 'created', + models.DateTimeField(auto_now_add=True, verbose_name='Created'), + ), + ( + 'key', + models.CharField( + db_index=True, + default=users.models.default_token, + max_length=100, + unique=True, + validators=[django.core.validators.MinLengthValidator(50)], + verbose_name='Key', + ), + ), + ( + 'name', + models.CharField( + blank=True, + help_text='Custom token name', + max_length=100, + verbose_name='Token Name', + ), + ), + ( + 'expiry', + models.DateField( + default=users.models.default_token_expiry, + help_text='Token expiry date', + verbose_name='Expiry Date', + ), + ), + ( + 'revoked', + models.BooleanField( + default=False, + help_text='Token has been revoked', + verbose_name='Revoked', + ), + ), + ( + 'user', + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name='api_tokens', + to=settings.AUTH_USER_MODEL, + verbose_name='User', + ), + ), + ( + 'last_seen', + models.DateField( + blank=True, + help_text='Last time the token was used', + null=True, + verbose_name='Last Seen', + ), + ), + ( + 'metadata', + models.JSONField( + blank=True, + help_text='JSON metadata field, for use by external plugins', + null=True, + verbose_name='Plugin Metadata', + ), + ), + ], + options={ + 'verbose_name': 'API Token', + 'verbose_name_plural': 'API Tokens', + 'abstract': False, + 'unique_together': set(), + }, + ), + migrations.CreateModel( + name='UserProfile', + fields=[ + ( + 'id', + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name='ID', + ), + ), + ( + 'metadata', + models.JSONField( + blank=True, + help_text='JSON metadata field, for use by external plugins', + null=True, + verbose_name='Plugin Metadata', + ), + ), + ( + 'language', + models.CharField( + blank=True, + help_text='Preferred language for the user', + max_length=10, + null=True, + verbose_name='Language', + ), + ), + ( + 'theme', + models.JSONField( + blank=True, + help_text='Settings for the web UI as JSON - do not edit manually!', + null=True, + verbose_name='Theme', + ), + ), + ( + 'widgets', + models.JSONField( + blank=True, + help_text='Settings for the dashboard widgets as JSON - do not edit manually!', + null=True, + verbose_name='Widgets', + ), + ), + ( + 'displayname', + models.CharField( + blank=True, + help_text='Chosen display name for the user', + max_length=255, + null=True, + verbose_name='Display Name', + ), + ), + ( + 'position', + models.CharField( + blank=True, + help_text='Main job title or position', + max_length=255, + null=True, + verbose_name='Position', + ), + ), + ( + 'status', + models.CharField( + blank=True, + help_text='User status message', + max_length=2000, + null=True, + verbose_name='Status', + ), + ), + ( + 'location', + models.CharField( + blank=True, + help_text='User location information', + max_length=2000, + null=True, + verbose_name='Location', + ), + ), + ( + 'active', + models.BooleanField( + default=True, + help_text='User is actively using the system', + verbose_name='Active', + ), + ), + ( + 'contact', + models.CharField( + blank=True, + help_text='Preferred contact information for the user', + max_length=255, + null=True, + verbose_name='Contact', + ), + ), + ( + 'type', + models.CharField( + choices=[ + ('bot', 'Bot'), + ('internal', 'Internal'), + ('external', 'External'), + ('guest', 'Guest'), + ], + default='internal', + help_text='Which type of user is this?', + max_length=10, + verbose_name='User Type', + ), + ), + ( + 'organisation', + models.CharField( + blank=True, + help_text='Users primary organisation/affiliation', + max_length=255, + null=True, + verbose_name='Organisation', + ), + ), + ( + 'primary_group', + models.ForeignKey( + blank=True, + help_text='Primary group for the user', + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name='primary_users', + to='auth.group', + verbose_name='Primary Group', + ), + ), + ( + 'user', + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + related_name='profile', + to=settings.AUTH_USER_MODEL, + verbose_name='User', + ), + ), + ], + options={'abstract': False}, + ) + ] diff --git a/src/backend/InvenTree/users/migrations/0002_auto_20201004_0158.py b/src/backend/InvenTree/users/migrations/0002_auto_20201004_0158.py deleted file mode 100644 index a0573a89be..0000000000 --- a/src/backend/InvenTree/users/migrations/0002_auto_20201004_0158.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.7 on 2020-10-04 01:58 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='ruleset', - name='name', - field=models.CharField(choices=[('admin', 'Admin'), ('part', 'Parts'), ('stock', 'Stock'), ('build', 'Build Orders'), ('purchase_order', 'Purchase Orders'), ('sales_order', 'Sales Orders')], help_text='Permission set', max_length=50), - ), - ] diff --git a/src/backend/InvenTree/users/migrations/0003_auto_20201005_2227.py b/src/backend/InvenTree/users/migrations/0003_auto_20201005_2227.py deleted file mode 100644 index 92d7e341fa..0000000000 --- a/src/backend/InvenTree/users/migrations/0003_auto_20201005_2227.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.0.7 on 2020-10-05 22:27 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0002_auto_20201004_0158'), - ] - - operations = [ - migrations.AlterField( - model_name='ruleset', - name='can_add', - field=models.BooleanField(default=False, help_text='Permission to add items', verbose_name='Add'), - ), - migrations.AlterField( - model_name='ruleset', - name='can_change', - field=models.BooleanField(default=False, help_text='Permissions to edit items', verbose_name='Change'), - ), - ] diff --git a/src/backend/InvenTree/users/migrations/0004_auto_20210113_1909.py b/src/backend/InvenTree/users/migrations/0004_auto_20210113_1909.py deleted file mode 100644 index d762dc4c08..0000000000 --- a/src/backend/InvenTree/users/migrations/0004_auto_20210113_1909.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.0.7 on 2021-01-13 19:09 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0003_auto_20201005_2227'), - ] - - operations = [ - migrations.AlterField( - model_name='ruleset', - name='name', - field=models.CharField(choices=[('admin', 'Admin'), ('part_category', 'Part Categories'), ('part', 'Parts'), ('stock_location', 'Stock Locations'), ('stock', 'Stock Items'), ('build', 'Build Orders'), ('purchase_order', 'Purchase Orders'), ('sales_order', 'Sales Orders')], help_text='Permission set', max_length=50), - ), - ] diff --git a/src/backend/InvenTree/users/migrations/0005_owner_model.py b/src/backend/InvenTree/users/migrations/0005_owner_model.py deleted file mode 100644 index 01ef66d9a3..0000000000 --- a/src/backend/InvenTree/users/migrations/0005_owner_model.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 3.0.7 on 2021-01-11 18:54 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('contenttypes', '0002_remove_content_type_name'), - ('users', '0004_auto_20210113_1909'), - ] - - operations = [ - migrations.CreateModel( - name='Owner', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('owner_id', models.PositiveIntegerField(blank=True, null=True)), - ('owner_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), - ], - ), - migrations.AddConstraint( - model_name='owner', - constraint=models.UniqueConstraint(fields=('owner_type', 'owner_id'), name='unique_owner'), - ), - ] diff --git a/src/backend/InvenTree/users/migrations/0006_alter_ruleset_name.py b/src/backend/InvenTree/users/migrations/0006_alter_ruleset_name.py deleted file mode 100644 index 0acf9404fa..0000000000 --- a/src/backend/InvenTree/users/migrations/0006_alter_ruleset_name.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.16 on 2023-02-16 22:22 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0005_owner_model'), - ] - - operations = [ - migrations.AlterField( - model_name='ruleset', - name='name', - field=models.CharField(choices=[('admin', 'Admin'), ('part_category', 'Part Categories'), ('part', 'Parts'), ('stocktake', 'Stocktake'), ('stock_location', 'Stock Locations'), ('stock', 'Stock Items'), ('build', 'Build Orders'), ('purchase_order', 'Purchase Orders'), ('sales_order', 'Sales Orders')], help_text='Permission set', max_length=50), - ), - ] diff --git a/src/backend/InvenTree/users/migrations/0007_alter_ruleset_name.py b/src/backend/InvenTree/users/migrations/0007_alter_ruleset_name.py deleted file mode 100644 index 64290bf118..0000000000 --- a/src/backend/InvenTree/users/migrations/0007_alter_ruleset_name.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.18 on 2023-03-14 10:07 - -from django.db import migrations, models -from users.ruleset import RULESET_CHOICES - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0006_alter_ruleset_name'), - ] - - operations = [ - migrations.AlterField( - model_name='ruleset', - name='name', - field=models.CharField(choices=RULESET_CHOICES, help_text='Permission set', max_length=50), - ), - ] diff --git a/src/backend/InvenTree/users/migrations/0008_apitoken.py b/src/backend/InvenTree/users/migrations/0008_apitoken.py deleted file mode 100644 index 52c4cc7edf..0000000000 --- a/src/backend/InvenTree/users/migrations/0008_apitoken.py +++ /dev/null @@ -1,35 +0,0 @@ -# Generated by Django 3.2.22 on 2023-10-20 01:09 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import users.models - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('users', '0007_alter_ruleset_name'), - ] - - operations = [ - migrations.CreateModel( - name='ApiToken', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Created')), - ('key', models.CharField(db_index=True, default=users.models.default_token, max_length=100, unique=True, verbose_name='Key')), - ('name', models.CharField(blank=True, help_text='Custom token name', max_length=100, verbose_name='Token Name')), - ('expiry', models.DateField(default=users.models.default_token_expiry, help_text='Token expiry date', verbose_name='Expiry Date')), - ('revoked', models.BooleanField(default=False, help_text='Token has been revoked', verbose_name='Revoked')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='api_tokens', to=settings.AUTH_USER_MODEL, verbose_name='User')), - ], - options={ - 'verbose_name': 'API Token', - 'verbose_name_plural': 'API Tokens', - 'abstract': False, - 'unique_together': {('user', 'name')}, - }, - ), - ] diff --git a/src/backend/InvenTree/users/migrations/0009_auto_20231020_2356.py b/src/backend/InvenTree/users/migrations/0009_auto_20231020_2356.py deleted file mode 100644 index 7e1300a68d..0000000000 --- a/src/backend/InvenTree/users/migrations/0009_auto_20231020_2356.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 3.2.21 on 2023-10-20 23:56 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0008_apitoken'), - ] - - operations = [ - migrations.AddField( - model_name='apitoken', - name='last_seen', - field=models.DateField(blank=True, help_text='Last time the token was used', null=True, verbose_name='Last Seen'), - ), - migrations.AddField( - model_name='apitoken', - name='metadata', - field=models.JSONField(blank=True, help_text='JSON metadata field, for use by external plugins', null=True, verbose_name='Plugin Metadata'), - ), - migrations.AlterUniqueTogether( - name='apitoken', - unique_together=set(), - ), - ] diff --git a/src/backend/InvenTree/users/migrations/0010_alter_apitoken_key.py b/src/backend/InvenTree/users/migrations/0010_alter_apitoken_key.py deleted file mode 100644 index ab45a9fa47..0000000000 --- a/src/backend/InvenTree/users/migrations/0010_alter_apitoken_key.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.23 on 2023-11-09 00:39 - -import django.core.validators -from django.db import migrations, models -import users.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0009_auto_20231020_2356'), - ] - - operations = [ - migrations.AlterField( - model_name='apitoken', - name='key', - field=models.CharField(db_index=True, default=users.models.default_token, max_length=100, unique=True, validators=[django.core.validators.MinLengthValidator(50)], verbose_name='Key'), - ), - ] diff --git a/src/backend/InvenTree/users/migrations/0011_auto_20240523_1640.py b/src/backend/InvenTree/users/migrations/0011_auto_20240523_1640.py deleted file mode 100644 index 428edaecfd..0000000000 --- a/src/backend/InvenTree/users/migrations/0011_auto_20240523_1640.py +++ /dev/null @@ -1,41 +0,0 @@ -# Generated by Django 4.2.12 on 2024-05-23 16:40 - -import structlog - -from importlib import import_module - -from django.conf import settings -from django.db import migrations - - -logger = structlog.getLogger('inventree') - - -def clear_sessions(apps, schema_editor): # pragma: no cover - """Clear all user sessions.""" - - # Ignore in test mode - if settings.TESTING: - return - - try: - engine = import_module(settings.SESSION_ENGINE) - engine.SessionStore.clear_expired() - logger.info('Cleared all user sessions to deal with GHSA-2crp-q9pc-457j') - except Exception: - # Database may not be ready yet, so this does not matter anyhow - pass - -class Migration(migrations.Migration): - - atomic = False - - dependencies = [ - ("users", "0010_alter_apitoken_key"), - ] - - operations = [ - migrations.RunPython( - clear_sessions, reverse_code=migrations.RunPython.noop, - ) - ] diff --git a/src/backend/InvenTree/users/migrations/0012_alter_ruleset_can_view.py b/src/backend/InvenTree/users/migrations/0012_alter_ruleset_can_view.py deleted file mode 100644 index fbe0a7bebd..0000000000 --- a/src/backend/InvenTree/users/migrations/0012_alter_ruleset_can_view.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 4.2.12 on 2024-07-18 21:19 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("users", "0011_auto_20240523_1640"), - ] - - operations = [ - migrations.AlterField( - model_name="ruleset", - name="can_view", - field=models.BooleanField( - default=False, help_text="Permission to view items", verbose_name="View" - ), - ), - ] diff --git a/src/backend/InvenTree/users/migrations/0013_migrate_mfa_20240408_1659.py b/src/backend/InvenTree/users/migrations/0013_migrate_mfa_20240408_1659.py deleted file mode 100644 index 85f48829b4..0000000000 --- a/src/backend/InvenTree/users/migrations/0013_migrate_mfa_20240408_1659.py +++ /dev/null @@ -1,48 +0,0 @@ -# Generated by Django 3.2.23 on 2024-01-19 16:59 - -import base64 - -from django.db import migrations - -from allauth.mfa.adapter import get_adapter -from allauth.mfa.models import Authenticator -from django_otp.plugins.otp_static.models import StaticDevice -from django_otp.plugins.otp_totp.models import TOTPDevice - - -def move_mfa(apps, schema_editor): - """Data migration to switch to django-allauth's new built-in MFA.""" - adapter = get_adapter() - authenticators = [] - for totp in TOTPDevice.objects.filter(confirmed=True).iterator(): - recovery_codes = set() - for sdevice in StaticDevice.objects.filter( - confirmed=True, user_id=totp.user_id - ).iterator(): - recovery_codes.update(sdevice.token_set.values_list('token', flat=True)) - secret = base64.b32encode(bytes.fromhex(totp.key)).decode('ascii') - totp_authenticator = Authenticator( - user_id=totp.user_id, - type=Authenticator.Type.TOTP, - data={'secret': adapter.encrypt(secret)}, - ) - authenticators.append(totp_authenticator) - authenticators.append( - Authenticator( - user_id=totp.user_id, - type=Authenticator.Type.RECOVERY_CODES, - data={'migrated_codes': [adapter.encrypt(c) for c in recovery_codes]}, - ) - ) - Authenticator.objects.bulk_create(authenticators) - - -class Migration(migrations.Migration): - dependencies = [('users', '0012_alter_ruleset_can_view'), - ('otp_static', '0002_throttling'), - ('otp_totp', '0002_auto_20190420_0723'), - ('mfa', '0002_authenticator_timestamps'),] - - operations = [ - migrations.RunPython(move_mfa, reverse_code=migrations.RunPython.noop) - ] diff --git a/src/backend/InvenTree/users/migrations/0014_userprofile.py b/src/backend/InvenTree/users/migrations/0014_userprofile.py deleted file mode 100644 index 3fb4502c38..0000000000 --- a/src/backend/InvenTree/users/migrations/0014_userprofile.py +++ /dev/null @@ -1,182 +0,0 @@ -# Generated by Django 4.2.19 on 2025-03-03 00:43 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - -def backfill_user_profiles(apps, schema_editor): - User = apps.get_model('auth', 'User') - UserProfile = apps.get_model('users', 'UserProfile') - for user in User.objects.all(): - UserProfile.objects.get_or_create(user=user) - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ("auth", "0012_alter_user_first_name_max_length"), - ("users", "0013_migrate_mfa_20240408_1659"), - ] - - operations = [ - migrations.CreateModel( - name="UserProfile", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "metadata", - models.JSONField( - blank=True, - help_text="JSON metadata field, for use by external plugins", - null=True, - verbose_name="Plugin Metadata", - ), - ), - ( - "language", - models.CharField( - blank=True, - help_text="Preferred language for the user", - max_length=10, - null=True, - verbose_name="Language", - ), - ), - ( - "theme", - models.JSONField( - blank=True, - help_text="Settings for the web UI as JSON - do not edit manually!", - null=True, - verbose_name="Theme", - ), - ), - ( - "widgets", - models.JSONField( - blank=True, - help_text="Settings for the dashboard widgets as JSON - do not edit manually!", - null=True, - verbose_name="Widgets", - ), - ), - ( - "displayname", - models.CharField( - blank=True, - help_text="Chosen display name for the user", - max_length=255, - null=True, - verbose_name="Display Name", - ), - ), - ( - "position", - models.CharField( - blank=True, - help_text="Main job title or position", - max_length=255, - null=True, - verbose_name="Position", - ), - ), - ( - "status", - models.CharField( - blank=True, - help_text="User status message", - max_length=2000, - null=True, - verbose_name="Status", - ), - ), - ( - "location", - models.CharField( - blank=True, - help_text="User location information", - max_length=2000, - null=True, - verbose_name="Location", - ), - ), - ( - "active", - models.BooleanField( - default=True, - help_text="User is actively using the system", - verbose_name="Active", - ), - ), - ( - "contact", - models.CharField( - blank=True, - help_text="Preferred contact information for the user", - max_length=255, - null=True, - verbose_name="Contact", - ), - ), - ( - "type", - models.CharField( - choices=[ - ("bot", "Bot"), - ("internal", "Internal"), - ("external", "External"), - ("guest", "Guest"), - ], - default="internal", - help_text="Which type of user is this?", - max_length=10, - verbose_name="Type", - ), - ), - ( - "organisation", - models.CharField( - blank=True, - help_text="Users primary organisation/affiliation", - max_length=255, - null=True, - verbose_name="Organisation", - ), - ), - ( - "primary_group", - models.ForeignKey( - blank=True, - help_text="Primary group for the user", - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="primary_users", - to="auth.group", - verbose_name="Primary Group", - ), - ), - ( - "user", - models.OneToOneField( - on_delete=django.db.models.deletion.CASCADE, - related_name="profile", - to=settings.AUTH_USER_MODEL, - verbose_name="User", - ), - ), - ], - options={ - "abstract": False, - }, - ), - migrations.RunPython(backfill_user_profiles), - ] diff --git a/src/backend/InvenTree/users/migrations/0015_alter_userprofile_type.py b/src/backend/InvenTree/users/migrations/0015_alter_userprofile_type.py deleted file mode 100644 index 0993b15178..0000000000 --- a/src/backend/InvenTree/users/migrations/0015_alter_userprofile_type.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 4.2.20 on 2025-04-07 20:53 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("users", "0014_userprofile"), - ] - - operations = [ - migrations.AlterField( - model_name="userprofile", - name="type", - field=models.CharField( - choices=[ - ("bot", "Bot"), - ("internal", "Internal"), - ("external", "External"), - ("guest", "Guest"), - ], - default="internal", - help_text="Which type of user is this?", - max_length=10, - verbose_name="User Type", - ), - ), - ] diff --git a/src/backend/InvenTree/users/test_migrations.py b/src/backend/InvenTree/users/test_migrations.py index 541ff2c893..9843b79177 100644 --- a/src/backend/InvenTree/users/test_migrations.py +++ b/src/backend/InvenTree/users/test_migrations.py @@ -24,77 +24,3 @@ class TestForwardMigrations(MigratorTestCase): User = self.new_state.apps.get_model('auth', 'user') self.assertEqual(User.objects.count(), 2) - - -class TestBackfillUserProfiles(MigratorTestCase): - """Test backfill migration for user profiles.""" - - migrate_from = ('users', '0012_alter_ruleset_can_view') - migrate_to = ('users', '0014_userprofile') - - def prepare(self): - """Setup the initial state of the database before migrations.""" - User = self.old_state.apps.get_model('auth', 'user') - - User.objects.create( - username='fred', email='fred@example.org', password='password' - ) - User.objects.create( - username='brad', email='brad@example.org', password='password' - ) - - def test_backfill_user_profiles(self): - """Test that user profiles are created during the migration.""" - User = self.new_state.apps.get_model('auth', 'user') - UserProfile = self.new_state.apps.get_model('users', 'UserProfile') - - self.assertEqual(User.objects.count(), 2) - self.assertEqual(UserProfile.objects.count(), 2) - - fred = User.objects.get(username='fred') - brad = User.objects.get(username='brad') - - self.assertIsNotNone(UserProfile.objects.get(user=fred)) - self.assertIsNotNone(UserProfile.objects.get(user=brad)) - - -class MFAMigrations(MigratorTestCase): - """Test entire schema migration sequence for the users app.""" - - # NOTE: otp_totp / otp_static are pinned explicitly (not just relying on - # 'users' 0012) because the merged cross-app migration plan that - # django_test_migrations truncates against is only incidentally ordered - - # unrelated migrations elsewhere in the project can shift whether these - # third-party app migrations land before or after 'users' 0012. - migrate_from = [ - ('users', '0012_alter_ruleset_can_view'), - ('otp_totp', '0002_auto_20190420_0723'), - ('otp_static', '0002_throttling'), - ] - migrate_to = ('users', '0013_migrate_mfa_20240408_1659') - - def prepare(self): - """Setup the initial state of the database before migrations.""" - User = self.old_state.apps.get_model('auth', 'user') - TOTPDevice = self.old_state.apps.get_model('otp_totp', 'TOTPDevice') - StaticDevice = self.old_state.apps.get_model('otp_static', 'StaticDevice') - - abc = User.objects.create( - username='fred', email='fred@fred.com', password='password' - ) - TOTPDevice.objects.create(user=abc, confirmed=True, key='1234') - abc1 = User.objects.create( - username='brad', email='brad@fred.com', password='password' - ) - TOTPDevice.objects.create(user=abc1, confirmed=False, key='1234') - StaticDevice.objects.create(user=abc1, confirmed=True) - - def test_users_exist(self): - """Test that users exist in the database.""" - User = self.new_state.apps.get_model('auth', 'user') - Authenticator = self.new_state.apps.get_model('mfa', 'Authenticator') - - self.assertEqual(User.objects.count(), 2) - # 2 Tokens - both for user 1 - self.assertEqual(Authenticator.objects.count(), 2) - self.assertEqual([1, 1], [i.user_id for i in Authenticator.objects.all()])