Merge 17922312fb into 5df2adae6d
This commit is contained in:
commit
6afc97822f
|
|
@ -0,0 +1,119 @@
|
||||||
|
# Generated by Django 5.2.13 on 2026-04-11 06:56
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("part", "0147_remove_part_default_supplier"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="PartRequirements",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.AutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"updated",
|
||||||
|
models.DateTimeField(
|
||||||
|
auto_now=True,
|
||||||
|
help_text="Timestamp of last update",
|
||||||
|
null=True,
|
||||||
|
verbose_name="Updated",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"in_stock",
|
||||||
|
models.DecimalField(
|
||||||
|
decimal_places=6,
|
||||||
|
default=0,
|
||||||
|
help_text="Total quantity of items in stock for this part",
|
||||||
|
max_digits=19,
|
||||||
|
verbose_name="In Stock",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"on_order",
|
||||||
|
models.DecimalField(
|
||||||
|
decimal_places=6,
|
||||||
|
default=0,
|
||||||
|
help_text="Total quantity of items on order for this part",
|
||||||
|
max_digits=19,
|
||||||
|
verbose_name="On Order",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"building",
|
||||||
|
models.DecimalField(
|
||||||
|
decimal_places=6,
|
||||||
|
default=0,
|
||||||
|
help_text="Total quantity of items currently being built for this part",
|
||||||
|
max_digits=19,
|
||||||
|
verbose_name="Building",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"build_order_requirements",
|
||||||
|
models.DecimalField(
|
||||||
|
decimal_places=6,
|
||||||
|
default=0,
|
||||||
|
help_text="Total quantity of this part required for all open build orders",
|
||||||
|
max_digits=19,
|
||||||
|
verbose_name="Build Order Requirements",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"sales_order_requirements",
|
||||||
|
models.DecimalField(
|
||||||
|
decimal_places=6,
|
||||||
|
default=0,
|
||||||
|
help_text="Total quantity of this part required for all open sales orders",
|
||||||
|
max_digits=19,
|
||||||
|
verbose_name="Sales Order Requirements",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"build_order_allocations",
|
||||||
|
models.DecimalField(
|
||||||
|
decimal_places=6,
|
||||||
|
default=0,
|
||||||
|
help_text="Total quantity of this part allocated to open build orders",
|
||||||
|
max_digits=19,
|
||||||
|
verbose_name="Build Order Allocations",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"sales_order_allocations",
|
||||||
|
models.DecimalField(
|
||||||
|
decimal_places=6,
|
||||||
|
default=0,
|
||||||
|
help_text="Total quantity of this part allocated to open sales orders",
|
||||||
|
max_digits=19,
|
||||||
|
verbose_name="Sales Order Allocations",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"part",
|
||||||
|
models.OneToOneField(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="requirements_data",
|
||||||
|
to="part.part",
|
||||||
|
verbose_name="part",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -2771,6 +2771,94 @@ def after_save_part(sender, instance: Part, created, **kwargs):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class PartRequirements(common.models.MetaMixin):
|
||||||
|
"""Model for caching the various "requirements" associated with a Part.
|
||||||
|
|
||||||
|
This information is cached because it is expensive to calculate "on the fly",
|
||||||
|
and does not change very often (compared to the number of times it is accessed).
|
||||||
|
|
||||||
|
The following attributes are cached against each part:
|
||||||
|
|
||||||
|
- in_stock: The total quantity of items in stock for this part
|
||||||
|
- on_order: The total quantity of items on order for this part
|
||||||
|
- building: The total quantity of items currently being built for this part
|
||||||
|
- build_order_requirements: The total quantity of items required for all open build orders
|
||||||
|
- sales_order_requirements: The total quantity of items required for all open sales orders
|
||||||
|
- build_order_allocations: The total quantity of items allocated to open build orders
|
||||||
|
- sales_order_allocations: The total quantity of items allocated to open sales orders
|
||||||
|
|
||||||
|
Note that each of these fields includes the quantity for any active variants of this part, if they exist.
|
||||||
|
"""
|
||||||
|
|
||||||
|
part = models.OneToOneField(
|
||||||
|
Part,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='requirements_data',
|
||||||
|
verbose_name=_('part'),
|
||||||
|
)
|
||||||
|
|
||||||
|
in_stock = models.DecimalField(
|
||||||
|
max_digits=19,
|
||||||
|
decimal_places=6,
|
||||||
|
default=0,
|
||||||
|
verbose_name=_('In Stock'),
|
||||||
|
help_text=_('Total quantity of items in stock for this part'),
|
||||||
|
)
|
||||||
|
|
||||||
|
on_order = models.DecimalField(
|
||||||
|
max_digits=19,
|
||||||
|
decimal_places=6,
|
||||||
|
default=0,
|
||||||
|
verbose_name=_('On Order'),
|
||||||
|
help_text=_('Total quantity of items on order for this part'),
|
||||||
|
)
|
||||||
|
|
||||||
|
building = models.DecimalField(
|
||||||
|
max_digits=19,
|
||||||
|
decimal_places=6,
|
||||||
|
default=0,
|
||||||
|
verbose_name=_('Building'),
|
||||||
|
help_text=_('Total quantity of items currently being built for this part'),
|
||||||
|
)
|
||||||
|
|
||||||
|
build_order_requirements = models.DecimalField(
|
||||||
|
max_digits=19,
|
||||||
|
decimal_places=6,
|
||||||
|
default=0,
|
||||||
|
verbose_name=_('Build Order Requirements'),
|
||||||
|
help_text=_('Total quantity of this part required for all open build orders'),
|
||||||
|
)
|
||||||
|
|
||||||
|
sales_order_requirements = models.DecimalField(
|
||||||
|
max_digits=19,
|
||||||
|
decimal_places=6,
|
||||||
|
default=0,
|
||||||
|
verbose_name=_('Sales Order Requirements'),
|
||||||
|
help_text=_('Total quantity of this part required for all open sales orders'),
|
||||||
|
)
|
||||||
|
|
||||||
|
build_order_allocations = models.DecimalField(
|
||||||
|
max_digits=19,
|
||||||
|
decimal_places=6,
|
||||||
|
default=0,
|
||||||
|
verbose_name=_('Build Order Allocations'),
|
||||||
|
help_text=_('Total quantity of this part allocated to open build orders'),
|
||||||
|
)
|
||||||
|
|
||||||
|
sales_order_allocations = models.DecimalField(
|
||||||
|
max_digits=19,
|
||||||
|
decimal_places=6,
|
||||||
|
default=0,
|
||||||
|
verbose_name=_('Sales Order Allocations'),
|
||||||
|
help_text=_('Total quantity of this part allocated to open sales orders'),
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_valid(self):
|
||||||
|
"""Return True if the cached requirements data is valid."""
|
||||||
|
return self.updated is not None
|
||||||
|
|
||||||
|
|
||||||
class PartPricing(common.models.MetaMixin):
|
class PartPricing(common.models.MetaMixin):
|
||||||
"""Model for caching min/max pricing information for a particular Part.
|
"""Model for caching min/max pricing information for a particular Part.
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue