feat: add edit/sync support for Material Types + Material Quantities
This commit is contained in:
parent
6bc5f06f7a
commit
dfc8dc0de3
20 changed files with 1075 additions and 86 deletions
|
|
@ -177,6 +177,48 @@ class WuttaFarmAppHandler(base.AppHandler):
|
||||||
with self.short_session(session=session) as sess:
|
with self.short_session(session=session) as sess:
|
||||||
return sess.query(model.Unit).order_by(model.Unit.name).all()
|
return sess.query(model.Unit).order_by(model.Unit.name).all()
|
||||||
|
|
||||||
|
def get_material_types(self, session=None):
|
||||||
|
"""
|
||||||
|
Returns a list of all known material types.
|
||||||
|
"""
|
||||||
|
model = self.model
|
||||||
|
with self.short_session(session=session) as sess:
|
||||||
|
return (
|
||||||
|
sess.query(model.MaterialType).order_by(model.MaterialType.name).all()
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_quantity_models(self):
|
||||||
|
model = self.model
|
||||||
|
return {
|
||||||
|
"standard": model.StandardQuantity,
|
||||||
|
"material": model.MaterialQuantity,
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_true_quantity(self, quantity, require=True):
|
||||||
|
model = self.model
|
||||||
|
if not isinstance(quantity, model.Quantity):
|
||||||
|
if require and not quantity:
|
||||||
|
raise ValueError(f"quantity is not valid: {quantity}")
|
||||||
|
return quantity
|
||||||
|
|
||||||
|
session = self.get_session(quantity)
|
||||||
|
models = self.get_quantity_models()
|
||||||
|
if require and quantity.quantity_type_id not in models:
|
||||||
|
raise ValueError(
|
||||||
|
f"quantity has invalid quantity_type_id: {quantity.quantity_type_id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
true_quantity = session.get(models[quantity.quantity_type_id], quantity.uuid)
|
||||||
|
if require and not true_quantity:
|
||||||
|
raise ValueError(f"quantity has no true/typed quantity record: {quantity}")
|
||||||
|
|
||||||
|
return true_quantity
|
||||||
|
|
||||||
|
def make_true_quantity(self, quantity_type_id, **kwargs):
|
||||||
|
models = self.get_quantity_models()
|
||||||
|
kwargs["quantity_type_id"] = quantity_type_id
|
||||||
|
return models[quantity_type_id](**kwargs)
|
||||||
|
|
||||||
def auto_sync_to_farmos(self, obj, model_name=None, client=None, require=True):
|
def auto_sync_to_farmos(self, obj, model_name=None, client=None, require=True):
|
||||||
"""
|
"""
|
||||||
Export the given object to farmOS, using configured handler.
|
Export the given object to farmOS, using configured handler.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,211 @@
|
||||||
|
"""add MaterialQuantity
|
||||||
|
|
||||||
|
Revision ID: 9c53513f8862
|
||||||
|
Revises: 1c89f3fbb521
|
||||||
|
Create Date: 2026-03-08 18:14:05.587678
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
import wuttjamaican.db.util
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = "9c53513f8862"
|
||||||
|
down_revision: Union[str, None] = "1c89f3fbb521"
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
|
||||||
|
# quantity_material
|
||||||
|
op.create_table(
|
||||||
|
"quantity_material",
|
||||||
|
sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["uuid"], ["quantity.uuid"], name=op.f("fk_quantity_material_uuid_quantity")
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint("uuid", name=op.f("pk_quantity_material")),
|
||||||
|
)
|
||||||
|
op.create_table(
|
||||||
|
"quantity_material_version",
|
||||||
|
sa.Column(
|
||||||
|
"uuid", wuttjamaican.db.util.UUID(), autoincrement=False, nullable=False
|
||||||
|
),
|
||||||
|
sa.Column(
|
||||||
|
"transaction_id", sa.BigInteger(), autoincrement=False, nullable=False
|
||||||
|
),
|
||||||
|
sa.Column("end_transaction_id", sa.BigInteger(), nullable=True),
|
||||||
|
sa.Column("operation_type", sa.SmallInteger(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint(
|
||||||
|
"uuid", "transaction_id", name=op.f("pk_quantity_material_version")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
op.f("ix_quantity_material_version_end_transaction_id"),
|
||||||
|
"quantity_material_version",
|
||||||
|
["end_transaction_id"],
|
||||||
|
unique=False,
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
op.f("ix_quantity_material_version_operation_type"),
|
||||||
|
"quantity_material_version",
|
||||||
|
["operation_type"],
|
||||||
|
unique=False,
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
"ix_quantity_material_version_pk_transaction_id",
|
||||||
|
"quantity_material_version",
|
||||||
|
["uuid", sa.literal_column("transaction_id DESC")],
|
||||||
|
unique=False,
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
"ix_quantity_material_version_pk_validity",
|
||||||
|
"quantity_material_version",
|
||||||
|
["uuid", "transaction_id", "end_transaction_id"],
|
||||||
|
unique=False,
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
op.f("ix_quantity_material_version_transaction_id"),
|
||||||
|
"quantity_material_version",
|
||||||
|
["transaction_id"],
|
||||||
|
unique=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
# quantity_material_material_type
|
||||||
|
op.create_table(
|
||||||
|
"quantity_material_material_type",
|
||||||
|
sa.Column("uuid", wuttjamaican.db.util.UUID(), nullable=False),
|
||||||
|
sa.Column("quantity_uuid", wuttjamaican.db.util.UUID(), nullable=False),
|
||||||
|
sa.Column("material_type_uuid", wuttjamaican.db.util.UUID(), nullable=False),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["material_type_uuid"],
|
||||||
|
["material_type.uuid"],
|
||||||
|
name=op.f(
|
||||||
|
"fk_quantity_material_material_type_material_type_uuid_material_type"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
sa.ForeignKeyConstraint(
|
||||||
|
["quantity_uuid"],
|
||||||
|
["quantity_material.uuid"],
|
||||||
|
name=op.f(
|
||||||
|
"fk_quantity_material_material_type_quantity_uuid_quantity_material"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint(
|
||||||
|
"uuid", name=op.f("pk_quantity_material_material_type")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
op.create_table(
|
||||||
|
"quantity_material_material_type_version",
|
||||||
|
sa.Column(
|
||||||
|
"uuid", wuttjamaican.db.util.UUID(), autoincrement=False, nullable=False
|
||||||
|
),
|
||||||
|
sa.Column(
|
||||||
|
"quantity_uuid",
|
||||||
|
wuttjamaican.db.util.UUID(),
|
||||||
|
autoincrement=False,
|
||||||
|
nullable=True,
|
||||||
|
),
|
||||||
|
sa.Column(
|
||||||
|
"material_type_uuid",
|
||||||
|
wuttjamaican.db.util.UUID(),
|
||||||
|
autoincrement=False,
|
||||||
|
nullable=True,
|
||||||
|
),
|
||||||
|
sa.Column(
|
||||||
|
"transaction_id", sa.BigInteger(), autoincrement=False, nullable=False
|
||||||
|
),
|
||||||
|
sa.Column("end_transaction_id", sa.BigInteger(), nullable=True),
|
||||||
|
sa.Column("operation_type", sa.SmallInteger(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint(
|
||||||
|
"uuid",
|
||||||
|
"transaction_id",
|
||||||
|
name=op.f("pk_quantity_material_material_type_version"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
op.f("ix_quantity_material_material_type_version_end_transaction_id"),
|
||||||
|
"quantity_material_material_type_version",
|
||||||
|
["end_transaction_id"],
|
||||||
|
unique=False,
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
op.f("ix_quantity_material_material_type_version_operation_type"),
|
||||||
|
"quantity_material_material_type_version",
|
||||||
|
["operation_type"],
|
||||||
|
unique=False,
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
"ix_quantity_material_material_type_version_pk_transaction_id",
|
||||||
|
"quantity_material_material_type_version",
|
||||||
|
["uuid", sa.literal_column("transaction_id DESC")],
|
||||||
|
unique=False,
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
"ix_quantity_material_material_type_version_pk_validity",
|
||||||
|
"quantity_material_material_type_version",
|
||||||
|
["uuid", "transaction_id", "end_transaction_id"],
|
||||||
|
unique=False,
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
op.f("ix_quantity_material_material_type_version_transaction_id"),
|
||||||
|
"quantity_material_material_type_version",
|
||||||
|
["transaction_id"],
|
||||||
|
unique=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
|
||||||
|
# quantity_material_material_type
|
||||||
|
op.drop_index(
|
||||||
|
op.f("ix_quantity_material_material_type_version_transaction_id"),
|
||||||
|
table_name="quantity_material_material_type_version",
|
||||||
|
)
|
||||||
|
op.drop_index(
|
||||||
|
"ix_quantity_material_material_type_version_pk_validity",
|
||||||
|
table_name="quantity_material_material_type_version",
|
||||||
|
)
|
||||||
|
op.drop_index(
|
||||||
|
"ix_quantity_material_material_type_version_pk_transaction_id",
|
||||||
|
table_name="quantity_material_material_type_version",
|
||||||
|
)
|
||||||
|
op.drop_index(
|
||||||
|
op.f("ix_quantity_material_material_type_version_operation_type"),
|
||||||
|
table_name="quantity_material_material_type_version",
|
||||||
|
)
|
||||||
|
op.drop_index(
|
||||||
|
op.f("ix_quantity_material_material_type_version_end_transaction_id"),
|
||||||
|
table_name="quantity_material_material_type_version",
|
||||||
|
)
|
||||||
|
op.drop_table("quantity_material_material_type_version")
|
||||||
|
op.drop_table("quantity_material_material_type")
|
||||||
|
|
||||||
|
# quantity_material
|
||||||
|
op.drop_index(
|
||||||
|
op.f("ix_quantity_material_version_transaction_id"),
|
||||||
|
table_name="quantity_material_version",
|
||||||
|
)
|
||||||
|
op.drop_index(
|
||||||
|
"ix_quantity_material_version_pk_validity",
|
||||||
|
table_name="quantity_material_version",
|
||||||
|
)
|
||||||
|
op.drop_index(
|
||||||
|
"ix_quantity_material_version_pk_transaction_id",
|
||||||
|
table_name="quantity_material_version",
|
||||||
|
)
|
||||||
|
op.drop_index(
|
||||||
|
op.f("ix_quantity_material_version_operation_type"),
|
||||||
|
table_name="quantity_material_version",
|
||||||
|
)
|
||||||
|
op.drop_index(
|
||||||
|
op.f("ix_quantity_material_version_end_transaction_id"),
|
||||||
|
table_name="quantity_material_version",
|
||||||
|
)
|
||||||
|
op.drop_table("quantity_material_version")
|
||||||
|
op.drop_table("quantity_material")
|
||||||
|
|
@ -32,7 +32,13 @@ from .users import WuttaFarmUser
|
||||||
# wuttafarm proper models
|
# wuttafarm proper models
|
||||||
from .unit import Unit, Measure
|
from .unit import Unit, Measure
|
||||||
from .material_type import MaterialType
|
from .material_type import MaterialType
|
||||||
from .quantities import QuantityType, Quantity, StandardQuantity
|
from .quantities import (
|
||||||
|
QuantityType,
|
||||||
|
Quantity,
|
||||||
|
StandardQuantity,
|
||||||
|
MaterialQuantity,
|
||||||
|
MaterialQuantityMaterialType,
|
||||||
|
)
|
||||||
from .asset import AssetType, Asset, AssetParent
|
from .asset import AssetType, Asset, AssetParent
|
||||||
from .asset_land import LandType, LandAsset
|
from .asset_land import LandType, LandAsset
|
||||||
from .asset_structure import StructureType, StructureAsset
|
from .asset_structure import StructureType, StructureAsset
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ Model definition for Material Types
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy import orm
|
||||||
|
from sqlalchemy.ext.associationproxy import association_proxy
|
||||||
|
|
||||||
from wuttjamaican.db import model
|
from wuttjamaican.db import model
|
||||||
|
|
||||||
|
|
@ -76,5 +78,23 @@ class MaterialType(model.Base):
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_quantities = orm.relationship(
|
||||||
|
"MaterialQuantityMaterialType",
|
||||||
|
cascade="all, delete-orphan",
|
||||||
|
cascade_backrefs=False,
|
||||||
|
back_populates="material_type",
|
||||||
|
)
|
||||||
|
|
||||||
|
def _make_material_quantity(qty):
|
||||||
|
from wuttafarm.db.model import MaterialQuantityMaterialType
|
||||||
|
|
||||||
|
return MaterialQuantityMaterialType(quantity=qty)
|
||||||
|
|
||||||
|
quantities = association_proxy(
|
||||||
|
"_quantities",
|
||||||
|
"quantity",
|
||||||
|
creator=_make_material_quantity,
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name or ""
|
return self.name or ""
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,9 @@ class QuantityMixin:
|
||||||
cascade_backrefs=False,
|
cascade_backrefs=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_value_decimal(self):
|
||||||
|
return self.quantity.get_value_decimal()
|
||||||
|
|
||||||
def render_as_text(self, config=None):
|
def render_as_text(self, config=None):
|
||||||
return self.quantity.render_as_text(config)
|
return self.quantity.render_as_text(config)
|
||||||
|
|
||||||
|
|
@ -249,3 +252,64 @@ class StandardQuantity(QuantityMixin, model.Base):
|
||||||
|
|
||||||
|
|
||||||
add_quantity_proxies(StandardQuantity)
|
add_quantity_proxies(StandardQuantity)
|
||||||
|
|
||||||
|
|
||||||
|
class MaterialQuantity(QuantityMixin, model.Base):
|
||||||
|
"""
|
||||||
|
Represents a Material Quantity from farmOS
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "quantity_material"
|
||||||
|
__versioned__ = {}
|
||||||
|
__wutta_hint__ = {
|
||||||
|
"model_title": "Material Quantity",
|
||||||
|
"model_title_plural": "Material Quantities",
|
||||||
|
"farmos_quantity_type": "material",
|
||||||
|
}
|
||||||
|
|
||||||
|
_material_types = orm.relationship(
|
||||||
|
"MaterialQuantityMaterialType",
|
||||||
|
cascade="all, delete-orphan",
|
||||||
|
cascade_backrefs=False,
|
||||||
|
back_populates="quantity",
|
||||||
|
)
|
||||||
|
|
||||||
|
material_types = association_proxy(
|
||||||
|
"_material_types",
|
||||||
|
"material_type",
|
||||||
|
creator=lambda mtype: MaterialQuantityMaterialType(material_type=mtype),
|
||||||
|
)
|
||||||
|
|
||||||
|
def render_as_text(self, config=None):
|
||||||
|
text = super().render_as_text(config)
|
||||||
|
mtypes = ", ".join([str(mt) for mt in self.material_types])
|
||||||
|
return f"{mtypes} {text}"
|
||||||
|
|
||||||
|
|
||||||
|
add_quantity_proxies(MaterialQuantity)
|
||||||
|
|
||||||
|
|
||||||
|
class MaterialQuantityMaterialType(model.Base):
|
||||||
|
"""
|
||||||
|
Represents a "material quantity's material type relationship" from
|
||||||
|
farmOS.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__tablename__ = "quantity_material_material_type"
|
||||||
|
__versioned__ = {}
|
||||||
|
|
||||||
|
uuid = model.uuid_column()
|
||||||
|
|
||||||
|
quantity_uuid = model.uuid_fk_column("quantity_material.uuid", nullable=False)
|
||||||
|
quantity = orm.relationship(
|
||||||
|
MaterialQuantity,
|
||||||
|
foreign_keys=quantity_uuid,
|
||||||
|
back_populates="_material_types",
|
||||||
|
)
|
||||||
|
|
||||||
|
material_type_uuid = model.uuid_fk_column("material_type.uuid", nullable=False)
|
||||||
|
material_type = orm.relationship(
|
||||||
|
"MaterialType",
|
||||||
|
foreign_keys=material_type_uuid,
|
||||||
|
back_populates="_quantities",
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -617,6 +617,49 @@ class ToFarmOSQuantity(ToFarmOS):
|
||||||
return payload
|
return payload
|
||||||
|
|
||||||
|
|
||||||
|
class MaterialQuantityImporter(ToFarmOSQuantity):
|
||||||
|
|
||||||
|
model_title = "MaterialQuantity"
|
||||||
|
farmos_quantity_type = "material"
|
||||||
|
|
||||||
|
def get_supported_fields(self):
|
||||||
|
fields = list(super().get_supported_fields())
|
||||||
|
fields.extend(
|
||||||
|
[
|
||||||
|
"material_types",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return fields
|
||||||
|
|
||||||
|
def normalize_target_object(self, quantity):
|
||||||
|
data = super().normalize_target_object(quantity)
|
||||||
|
|
||||||
|
if "material_types" in self.fields:
|
||||||
|
data["material_types"] = [
|
||||||
|
UUID(mtype["id"])
|
||||||
|
for mtype in quantity["relationships"]["material_type"]["data"]
|
||||||
|
]
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def get_quantity_payload(self, source_data):
|
||||||
|
payload = super().get_quantity_payload(source_data)
|
||||||
|
|
||||||
|
rels = {}
|
||||||
|
if "material_types" in self.fields:
|
||||||
|
rels["material_type"] = {"data": []}
|
||||||
|
for uuid in source_data["material_types"]:
|
||||||
|
rels["material_type"]["data"].append(
|
||||||
|
{
|
||||||
|
"id": str(uuid),
|
||||||
|
"type": "taxonomy_term--material_type",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
payload.setdefault("relationships", {}).update(rels)
|
||||||
|
return payload
|
||||||
|
|
||||||
|
|
||||||
class StandardQuantityImporter(ToFarmOSQuantity):
|
class StandardQuantityImporter(ToFarmOSQuantity):
|
||||||
|
|
||||||
model_title = "StandardQuantity"
|
model_title = "StandardQuantity"
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,7 @@ class FromWuttaFarmToFarmOS(FromWuttaFarmHandler, ToFarmOSHandler):
|
||||||
importers["PlantAsset"] = PlantAssetImporter
|
importers["PlantAsset"] = PlantAssetImporter
|
||||||
importers["Unit"] = UnitImporter
|
importers["Unit"] = UnitImporter
|
||||||
importers["MaterialType"] = MaterialTypeImporter
|
importers["MaterialType"] = MaterialTypeImporter
|
||||||
|
importers["MaterialQuantity"] = MaterialQuantityImporter
|
||||||
importers["StandardQuantity"] = StandardQuantityImporter
|
importers["StandardQuantity"] = StandardQuantityImporter
|
||||||
importers["ActivityLog"] = ActivityLogImporter
|
importers["ActivityLog"] = ActivityLogImporter
|
||||||
importers["HarvestLog"] = HarvestLogImporter
|
importers["HarvestLog"] = HarvestLogImporter
|
||||||
|
|
@ -449,6 +450,24 @@ class FromWuttaFarmQuantity(FromWuttaFarm):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MaterialQuantityImporter(
|
||||||
|
FromWuttaFarmQuantity, farmos_importing.model.MaterialQuantityImporter
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
WuttaFarm → farmOS API exporter for Material Quantities
|
||||||
|
"""
|
||||||
|
|
||||||
|
source_model_class = model.MaterialQuantity
|
||||||
|
|
||||||
|
def normalize_source_object(self, quantity):
|
||||||
|
data = super().normalize_source_object(quantity)
|
||||||
|
|
||||||
|
if "material_types" in self.fields:
|
||||||
|
data["material_types"] = [mt.farmos_uuid for mt in quantity.material_types]
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class StandardQuantityImporter(
|
class StandardQuantityImporter(
|
||||||
FromWuttaFarmQuantity, farmos_importing.model.StandardQuantityImporter
|
FromWuttaFarmQuantity, farmos_importing.model.StandardQuantityImporter
|
||||||
):
|
):
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,7 @@ class FromFarmOSToWuttaFarm(FromFarmOSHandler, ToWuttaFarmHandler):
|
||||||
importers["MaterialType"] = MaterialTypeImporter
|
importers["MaterialType"] = MaterialTypeImporter
|
||||||
importers["QuantityType"] = QuantityTypeImporter
|
importers["QuantityType"] = QuantityTypeImporter
|
||||||
importers["StandardQuantity"] = StandardQuantityImporter
|
importers["StandardQuantity"] = StandardQuantityImporter
|
||||||
|
importers["MaterialQuantity"] = MaterialQuantityImporter
|
||||||
importers["LogType"] = LogTypeImporter
|
importers["LogType"] = LogTypeImporter
|
||||||
importers["ActivityLog"] = ActivityLogImporter
|
importers["ActivityLog"] = ActivityLogImporter
|
||||||
importers["HarvestLog"] = HarvestLogImporter
|
importers["HarvestLog"] = HarvestLogImporter
|
||||||
|
|
@ -1419,3 +1420,76 @@ class StandardQuantityImporter(QuantityImporterBase):
|
||||||
"units_uuid",
|
"units_uuid",
|
||||||
"label",
|
"label",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class MaterialQuantityImporter(QuantityImporterBase):
|
||||||
|
"""
|
||||||
|
farmOS API → WuttaFarm importer for Material Quantities
|
||||||
|
"""
|
||||||
|
|
||||||
|
model_class = model.MaterialQuantity
|
||||||
|
|
||||||
|
def get_supported_fields(self):
|
||||||
|
fields = list(super().get_supported_fields())
|
||||||
|
fields.extend(
|
||||||
|
[
|
||||||
|
"material_types",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
return fields
|
||||||
|
|
||||||
|
def normalize_source_object(self, quantity):
|
||||||
|
""" """
|
||||||
|
data = super().normalize_source_object(quantity)
|
||||||
|
|
||||||
|
if "material_types" in self.fields:
|
||||||
|
data["material_types"] = [
|
||||||
|
UUID(mtype["id"])
|
||||||
|
for mtype in quantity["relationships"]["material_type"]["data"]
|
||||||
|
]
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def normalize_target_object(self, quantity):
|
||||||
|
data = super().normalize_target_object(quantity)
|
||||||
|
|
||||||
|
if "material_types" in self.fields:
|
||||||
|
data["material_types"] = [
|
||||||
|
mtype.farmos_uuid for mtype in quantity.material_types
|
||||||
|
]
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
def update_target_object(self, quantity, source_data, target_data=None):
|
||||||
|
model = self.app.model
|
||||||
|
quantity = super().update_target_object(quantity, source_data, target_data)
|
||||||
|
|
||||||
|
if "material_types" in self.fields:
|
||||||
|
if (
|
||||||
|
not target_data
|
||||||
|
or target_data["material_types"] != source_data["material_types"]
|
||||||
|
):
|
||||||
|
|
||||||
|
for farmos_uuid in source_data["material_types"]:
|
||||||
|
if (
|
||||||
|
not target_data
|
||||||
|
or farmos_uuid not in target_data["material_types"]
|
||||||
|
):
|
||||||
|
mtype = (
|
||||||
|
self.target_session.query(model.MaterialType)
|
||||||
|
.filter(model.MaterialType.farmos_uuid == farmos_uuid)
|
||||||
|
.one()
|
||||||
|
)
|
||||||
|
quantity.material_types.append(mtype)
|
||||||
|
|
||||||
|
if target_data:
|
||||||
|
for farmos_uuid in target_data["material_types"]:
|
||||||
|
if farmos_uuid not in source_data["material_types"]:
|
||||||
|
mtype = (
|
||||||
|
self.target_session.query(model.MaterialType)
|
||||||
|
.filter(model.MaterialType.farmos_uuid == farmos_uuid)
|
||||||
|
.one()
|
||||||
|
)
|
||||||
|
quantity.material_types.remove(mtype)
|
||||||
|
|
||||||
|
return quantity
|
||||||
|
|
|
||||||
|
|
@ -260,27 +260,29 @@ class Normalizer(GenericHandler):
|
||||||
|
|
||||||
measure_id = attrs["measure"]
|
measure_id = attrs["measure"]
|
||||||
|
|
||||||
quantity_objects.append(
|
quantity_object = {
|
||||||
{
|
"uuid": quantity["id"],
|
||||||
"uuid": quantity["id"],
|
"drupal_id": attrs["drupal_internal__id"],
|
||||||
"drupal_id": attrs["drupal_internal__id"],
|
"quantity_type_uuid": rels["quantity_type"]["data"]["id"],
|
||||||
"quantity_type_uuid": rels["quantity_type"]["data"][
|
"quantity_type_id": rels["quantity_type"]["data"]["meta"][
|
||||||
"id"
|
"drupal_internal__target_id"
|
||||||
],
|
],
|
||||||
"quantity_type_id": rels["quantity_type"]["data"][
|
"measure_id": measure_id,
|
||||||
"meta"
|
"measure_name": self.get_farmos_measure_name(measure_id),
|
||||||
]["drupal_internal__target_id"],
|
"value_numerator": value["numerator"],
|
||||||
"measure_id": measure_id,
|
"value_decimal": value["decimal"],
|
||||||
"measure_name": self.get_farmos_measure_name(
|
"value_denominator": value["denominator"],
|
||||||
measure_id
|
"unit_uuid": unit_uuid,
|
||||||
),
|
"unit_name": unit["attributes"]["name"],
|
||||||
"value_numerator": value["numerator"],
|
}
|
||||||
"value_decimal": value["decimal"],
|
if quantity_object["quantity_type_id"] == "material":
|
||||||
"value_denominator": value["denominator"],
|
quantity_object["material_types"] = [
|
||||||
"unit_uuid": unit_uuid,
|
{"uuid": mtype["id"]}
|
||||||
"unit_name": unit["attributes"]["name"],
|
for mtype in quantity["relationships"]["material_type"][
|
||||||
}
|
"data"
|
||||||
)
|
]
|
||||||
|
]
|
||||||
|
quantity_objects.append(quantity_object)
|
||||||
|
|
||||||
if owners := relationships.get("owner"):
|
if owners := relationships.get("owner"):
|
||||||
for user in owners["data"]:
|
for user in owners["data"]:
|
||||||
|
|
|
||||||
|
|
@ -164,10 +164,9 @@ class FarmOSRefs(WuttaSet):
|
||||||
self.route_prefix = route_prefix
|
self.route_prefix = route_prefix
|
||||||
|
|
||||||
def serialize(self, node, appstruct):
|
def serialize(self, node, appstruct):
|
||||||
if appstruct is colander.null:
|
if not appstruct:
|
||||||
return colander.null
|
return colander.null
|
||||||
|
return appstruct
|
||||||
return json.dumps(appstruct)
|
|
||||||
|
|
||||||
def widget_maker(self, **kwargs):
|
def widget_maker(self, **kwargs):
|
||||||
from wuttafarm.web.forms.widgets import FarmOSRefsWidget
|
from wuttafarm.web.forms.widgets import FarmOSRefsWidget
|
||||||
|
|
@ -288,6 +287,37 @@ class PlantTypeRefs(WuttaSet):
|
||||||
return PlantTypeRefsWidget(self.request, **kwargs)
|
return PlantTypeRefsWidget(self.request, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class MaterialTypeRefs(colander.List):
|
||||||
|
"""
|
||||||
|
Schema type for Material Types field (on a Material Asset).
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, request):
|
||||||
|
super().__init__()
|
||||||
|
self.request = request
|
||||||
|
self.config = self.request.wutta_config
|
||||||
|
self.app = self.config.get_app()
|
||||||
|
|
||||||
|
def serialize(self, node, appstruct):
|
||||||
|
if not appstruct:
|
||||||
|
return colander.null
|
||||||
|
|
||||||
|
mtypes = []
|
||||||
|
for mtype in appstruct:
|
||||||
|
mtypes.append(
|
||||||
|
{
|
||||||
|
"uuid": mtype.uuid.hex,
|
||||||
|
"name": mtype.name,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return mtypes
|
||||||
|
|
||||||
|
def widget_maker(self, **kwargs):
|
||||||
|
from wuttafarm.web.forms.widgets import MaterialTypeRefsWidget
|
||||||
|
|
||||||
|
return MaterialTypeRefsWidget(self.request, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class SeasonRefs(WuttaSet):
|
class SeasonRefs(WuttaSet):
|
||||||
"""
|
"""
|
||||||
Schema type for Plant Types field (on a Plant Asset).
|
Schema type for Plant Types field (on a Plant Asset).
|
||||||
|
|
@ -454,22 +484,36 @@ class QuantityRefs(colander.List):
|
||||||
|
|
||||||
quantities = []
|
quantities = []
|
||||||
for qty in appstruct:
|
for qty in appstruct:
|
||||||
quantities.append(
|
|
||||||
{
|
quantity = {
|
||||||
"uuid": qty.uuid.hex,
|
"uuid": qty.uuid.hex,
|
||||||
"quantity_type": {
|
"quantity_type": {
|
||||||
"id": qty.quantity_type_id,
|
"drupal_id": qty.quantity_type_id,
|
||||||
"name": qty.quantity_type.name,
|
"name": qty.quantity_type.name,
|
||||||
},
|
},
|
||||||
"measure": qty.measure_id,
|
"measure": qty.measure_id,
|
||||||
"value": qty.get_value_decimal(),
|
"value": qty.get_value_decimal(),
|
||||||
"units": {
|
"units": {
|
||||||
"uuid": qty.units.uuid.hex,
|
"uuid": qty.units.uuid.hex,
|
||||||
"name": qty.units.name,
|
"name": qty.units.name,
|
||||||
},
|
},
|
||||||
"as_text": qty.render_as_text(self.config),
|
"as_text": qty.render_as_text(self.config),
|
||||||
}
|
# nb. always include this regardless of quantity type,
|
||||||
)
|
# for sake of easier frontend logic
|
||||||
|
"material_types": [],
|
||||||
|
}
|
||||||
|
|
||||||
|
if qty.quantity_type_id == "material":
|
||||||
|
quantity["material_types"] = []
|
||||||
|
for mtype in qty.material_types:
|
||||||
|
quantity["material_types"].append(
|
||||||
|
{
|
||||||
|
"uuid": mtype.uuid.hex,
|
||||||
|
"name": mtype.name,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
quantities.append(quantity)
|
||||||
|
|
||||||
return quantities
|
return quantities
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ class FarmOSRefsWidget(Widget):
|
||||||
return HTML.tag("span")
|
return HTML.tag("span")
|
||||||
|
|
||||||
links = []
|
links = []
|
||||||
for obj in json.loads(cstruct):
|
for obj in cstruct:
|
||||||
url = self.request.route_url(
|
url = self.request.route_url(
|
||||||
f"{self.route_prefix}.view", uuid=obj["uuid"]
|
f"{self.route_prefix}.view", uuid=obj["uuid"]
|
||||||
)
|
)
|
||||||
|
|
@ -332,6 +332,72 @@ class PlantTypeRefsWidget(Widget):
|
||||||
return set(pstruct.split(","))
|
return set(pstruct.split(","))
|
||||||
|
|
||||||
|
|
||||||
|
class MaterialTypeRefsWidget(Widget):
|
||||||
|
"""
|
||||||
|
Widget for Material Types field (on a Material Asset).
|
||||||
|
"""
|
||||||
|
|
||||||
|
template = "materialtyperefs"
|
||||||
|
values = ()
|
||||||
|
|
||||||
|
def __init__(self, request, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.request = request
|
||||||
|
self.config = self.request.wutta_config
|
||||||
|
self.app = self.config.get_app()
|
||||||
|
|
||||||
|
def serialize(self, field, cstruct, **kw):
|
||||||
|
""" """
|
||||||
|
model = self.app.model
|
||||||
|
session = Session()
|
||||||
|
|
||||||
|
if not cstruct:
|
||||||
|
cstruct = []
|
||||||
|
|
||||||
|
if readonly := kw.get("readonly", self.readonly):
|
||||||
|
items = []
|
||||||
|
for mtype in cstruct:
|
||||||
|
items.append(
|
||||||
|
HTML.tag(
|
||||||
|
"li",
|
||||||
|
c=tags.link_to(
|
||||||
|
mtype["name"],
|
||||||
|
self.request.route_url(
|
||||||
|
"material_types.view", uuid=mtype["uuid"]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return HTML.tag("ul", c=items)
|
||||||
|
|
||||||
|
tmpl_values = self.get_template_values(field, cstruct, kw)
|
||||||
|
return field.renderer(self.template, **tmpl_values)
|
||||||
|
|
||||||
|
def get_template_values(self, field, cstruct, kw):
|
||||||
|
""" """
|
||||||
|
values = super().get_template_values(field, cstruct, kw)
|
||||||
|
session = Session()
|
||||||
|
|
||||||
|
material_types = []
|
||||||
|
for mtype in self.app.get_material_types(session):
|
||||||
|
material_types.append(
|
||||||
|
{
|
||||||
|
"uuid": mtype.uuid.hex,
|
||||||
|
"name": mtype.name,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
values["material_types"] = json.dumps(material_types)
|
||||||
|
|
||||||
|
return values
|
||||||
|
|
||||||
|
def deserialize(self, field, pstruct):
|
||||||
|
""" """
|
||||||
|
if not pstruct:
|
||||||
|
return []
|
||||||
|
|
||||||
|
return json.loads(pstruct)
|
||||||
|
|
||||||
|
|
||||||
class SeasonRefsWidget(Widget):
|
class SeasonRefsWidget(Widget):
|
||||||
"""
|
"""
|
||||||
Widget for Seasons field (on a Plant Asset).
|
Widget for Seasons field (on a Plant Asset).
|
||||||
|
|
@ -550,11 +616,10 @@ class QuantityRefsWidget(Widget):
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
quantities = []
|
quantities = []
|
||||||
|
|
||||||
for qty in cstruct:
|
for qty in cstruct:
|
||||||
# TODO: support more quantity types
|
|
||||||
url = self.request.route_url(
|
url = self.request.route_url(
|
||||||
"quantities_standard.view", uuid=qty["uuid"]
|
f"quantities_{qty['quantity_type']['drupal_id']}.view",
|
||||||
|
uuid=qty["uuid"],
|
||||||
)
|
)
|
||||||
quantities.append(HTML.tag("li", c=tags.link_to(qty["as_text"], url)))
|
quantities.append(HTML.tag("li", c=tags.link_to(qty["as_text"], url)))
|
||||||
|
|
||||||
|
|
@ -570,17 +635,25 @@ class QuantityRefsWidget(Widget):
|
||||||
|
|
||||||
qtypes = []
|
qtypes = []
|
||||||
for qtype in self.app.get_quantity_types(session):
|
for qtype in self.app.get_quantity_types(session):
|
||||||
# TODO: add support for other quantity types
|
qtypes.append(
|
||||||
if qtype.drupal_id == "standard":
|
{
|
||||||
qtypes.append(
|
"uuid": qtype.uuid.hex,
|
||||||
{
|
"drupal_id": qtype.drupal_id,
|
||||||
"uuid": qtype.uuid.hex,
|
"name": qtype.name,
|
||||||
"drupal_id": qtype.drupal_id,
|
}
|
||||||
"name": qtype.name,
|
)
|
||||||
}
|
|
||||||
)
|
|
||||||
values["quantity_types"] = qtypes
|
values["quantity_types"] = qtypes
|
||||||
|
|
||||||
|
material_types = []
|
||||||
|
for mtype in self.app.get_material_types(session):
|
||||||
|
material_types.append(
|
||||||
|
{
|
||||||
|
"uuid": mtype.uuid.hex,
|
||||||
|
"name": mtype.name,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
values["material_types"] = material_types
|
||||||
|
|
||||||
measures = []
|
measures = []
|
||||||
for measure in self.app.get_measures(session):
|
for measure in self.app.get_measures(session):
|
||||||
measures.append(
|
measures.append(
|
||||||
|
|
|
||||||
|
|
@ -182,6 +182,11 @@ class WuttaFarmMenuHandler(base.MenuHandler):
|
||||||
"route": "quantities",
|
"route": "quantities",
|
||||||
"perm": "quantities.list",
|
"perm": "quantities.list",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "Material Quantities",
|
||||||
|
"route": "quantities_material",
|
||||||
|
"perm": "quantities_material.list",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "Standard Quantities",
|
"title": "Standard Quantities",
|
||||||
"route": "quantities_standard",
|
"route": "quantities_standard",
|
||||||
|
|
@ -322,6 +327,11 @@ class WuttaFarmMenuHandler(base.MenuHandler):
|
||||||
"route": "farmos_quantity_types",
|
"route": "farmos_quantity_types",
|
||||||
"perm": "farmos_quantity_types.list",
|
"perm": "farmos_quantity_types.list",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "Material Quantities",
|
||||||
|
"route": "farmos_quantities_material",
|
||||||
|
"perm": "farmos_quantities_material.list",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "Standard Quantities",
|
"title": "Standard Quantities",
|
||||||
"route": "farmos_quantities_standard",
|
"route": "farmos_quantities_standard",
|
||||||
|
|
@ -451,6 +461,11 @@ class WuttaFarmMenuHandler(base.MenuHandler):
|
||||||
"route": "farmos_quantity_types",
|
"route": "farmos_quantity_types",
|
||||||
"perm": "farmos_quantity_types.list",
|
"perm": "farmos_quantity_types.list",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "Material Quantities",
|
||||||
|
"route": "farmos_quantities_material",
|
||||||
|
"perm": "farmos_quantities_material.list",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "Standard Quantities",
|
"title": "Standard Quantities",
|
||||||
"route": "farmos_quantities_standard",
|
"route": "farmos_quantities_standard",
|
||||||
|
|
|
||||||
13
src/wuttafarm/web/templates/deform/materialtyperefs.pt
Normal file
13
src/wuttafarm/web/templates/deform/materialtyperefs.pt
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<div tal:define="
|
||||||
|
name name|field.name;
|
||||||
|
oid oid|field.oid;
|
||||||
|
vmodel vmodel|'modelData.'+oid;
|
||||||
|
can_create can_create|False;"
|
||||||
|
tal:omit-tag="">
|
||||||
|
|
||||||
|
<material-types-picker tal:attributes="name name;
|
||||||
|
v-model vmodel;
|
||||||
|
:material-types material_types;
|
||||||
|
:can-create str(can_create).lower();" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
<quantities-editor tal:attributes="name name;
|
<quantities-editor tal:attributes="name name;
|
||||||
v-model vmodel;
|
v-model vmodel;
|
||||||
:quantity-types quantity_types;
|
:quantity-types quantity_types;
|
||||||
|
:material-types material_types;
|
||||||
:measures measures;
|
:measures measures;
|
||||||
:units units;" />
|
:units units;" />
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
<%def name="make_wuttafarm_components()">
|
<%def name="make_wuttafarm_components()">
|
||||||
${self.make_assets_picker_component()}
|
${self.make_assets_picker_component()}
|
||||||
${self.make_animal_type_picker_component()}
|
${self.make_animal_type_picker_component()}
|
||||||
|
${self.make_material_types_picker_component()}
|
||||||
${self.make_quantity_editor_component()}
|
${self.make_quantity_editor_component()}
|
||||||
${self.make_quantities_editor_component()}
|
${self.make_quantities_editor_component()}
|
||||||
${self.make_plant_types_picker_component()}
|
${self.make_plant_types_picker_component()}
|
||||||
|
|
@ -241,10 +242,134 @@
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_material_types_picker_component()">
|
||||||
|
<script type="text/x-template" id="material-types-picker-template">
|
||||||
|
<div>
|
||||||
|
<input type="hidden" :name="name" :value="JSON.stringify(value)" />
|
||||||
|
|
||||||
|
<div style="display: flex; gap: 0.5rem; align-items: center;">
|
||||||
|
|
||||||
|
<span>Add:</span>
|
||||||
|
|
||||||
|
<b-autocomplete v-model="addName"
|
||||||
|
ref="addName"
|
||||||
|
:data="addNameData"
|
||||||
|
field="name"
|
||||||
|
open-on-focus
|
||||||
|
keep-first
|
||||||
|
@select="addNameSelected"
|
||||||
|
clear-on-select
|
||||||
|
style="flex-grow: 1;">
|
||||||
|
<template #empty>No results found</template>
|
||||||
|
</b-autocomplete>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<${b}-table :data="materialTypeData">
|
||||||
|
|
||||||
|
<${b}-table-column field="name" v-slot="props">
|
||||||
|
<span>{{ props.row.name }}</span>
|
||||||
|
</${b}-table-column>
|
||||||
|
|
||||||
|
<${b}-table-column v-slot="props">
|
||||||
|
<a href="#"
|
||||||
|
class="has-text-danger"
|
||||||
|
@click.prevent="removeMaterialType(props.row)">
|
||||||
|
<i class="fas fa-trash" /> Remove
|
||||||
|
</a>
|
||||||
|
</${b}-table-column>
|
||||||
|
|
||||||
|
</${b}-table>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const MaterialTypesPicker = {
|
||||||
|
template: '#material-types-picker-template',
|
||||||
|
props: {
|
||||||
|
name: String,
|
||||||
|
value: Array,
|
||||||
|
materialTypes: Array,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
addName: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
materialTypeData() {
|
||||||
|
const data = []
|
||||||
|
|
||||||
|
if (this.value) {
|
||||||
|
|
||||||
|
const mtypes = new Map(this.materialTypes.map((mtype) => {
|
||||||
|
return [mtype.uuid, mtype.name]
|
||||||
|
}))
|
||||||
|
|
||||||
|
for (const mtype of this.value) {
|
||||||
|
if (mtypes.has(mtype.uuid)) {
|
||||||
|
data.push(mtype)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
addNameData() {
|
||||||
|
if (!this.addName) {
|
||||||
|
return this.materialTypes
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.materialTypes.filter((mtype) => {
|
||||||
|
return mtype.name.toLowerCase().indexOf(this.addName.toLowerCase()) >= 0
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
focus() {
|
||||||
|
this.$refs.addName.focus()
|
||||||
|
},
|
||||||
|
|
||||||
|
addNameSelected(option) {
|
||||||
|
if (!option) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = Array.from(this.value || [])
|
||||||
|
|
||||||
|
if (!value.includes(option)) {
|
||||||
|
value.push(option)
|
||||||
|
this.$emit('input', value)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addName = null
|
||||||
|
},
|
||||||
|
|
||||||
|
removeMaterialType(ptype) {
|
||||||
|
const value = Array.from(this.value)
|
||||||
|
const i = value.indexOf(ptype)
|
||||||
|
value.splice(i, 1)
|
||||||
|
this.$emit('input', value)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Vue.component('material-types-picker', MaterialTypesPicker)
|
||||||
|
<% request.register_component('material-types-picker', 'MaterialTypesPicker') %>
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
<%def name="make_quantity_editor_component()">
|
<%def name="make_quantity_editor_component()">
|
||||||
<script type="text/x-template" id="quantity-editor-template">
|
<script type="text/x-template" id="quantity-editor-template">
|
||||||
<div>
|
<div>
|
||||||
|
|
||||||
|
<b-field v-if="quantityType == 'material'"
|
||||||
|
label="Material Types"
|
||||||
|
horizontal>
|
||||||
|
<material-types-picker v-model="value.material_types"
|
||||||
|
ref="materials"
|
||||||
|
:material-types="materialTypes" />
|
||||||
|
</b-field>
|
||||||
|
|
||||||
<b-field label="Measure" horizontal
|
<b-field label="Measure" horizontal
|
||||||
## TODO: why is this needed?
|
## TODO: why is this needed?
|
||||||
style="margin-bottom: 1rem;">
|
style="margin-bottom: 1rem;">
|
||||||
|
|
@ -306,6 +431,8 @@
|
||||||
props: {
|
props: {
|
||||||
name: String,
|
name: String,
|
||||||
value: Object,
|
value: Object,
|
||||||
|
quantityType: String,
|
||||||
|
materialTypes: Array,
|
||||||
measures: Array,
|
measures: Array,
|
||||||
units: Array,
|
units: Array,
|
||||||
creating: {
|
creating: {
|
||||||
|
|
@ -314,8 +441,6 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
measure: this.value.measure,
|
measure: this.value.measure,
|
||||||
valueAmount: this.value.value,
|
valueAmount: this.value.value,
|
||||||
|
|
@ -349,8 +474,12 @@
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
focusMeasure() {
|
focusForCreate() {
|
||||||
this.$refs.measure.focus()
|
if (this.value.quantity_type.drupal_id == 'material') {
|
||||||
|
this.$refs.materials.focus()
|
||||||
|
} else {
|
||||||
|
this.$refs.measure.focus()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
focusValue() {
|
focusValue() {
|
||||||
|
|
@ -439,6 +568,8 @@
|
||||||
|
|
||||||
<template #detail="props">
|
<template #detail="props">
|
||||||
<quantity-editor v-model="props.row"
|
<quantity-editor v-model="props.row"
|
||||||
|
:quantity-type="props.row.quantity_type.drupal_id"
|
||||||
|
:material-types="materialTypes"
|
||||||
:measures="measures"
|
:measures="measures"
|
||||||
:units="units"
|
:units="units"
|
||||||
@save="editSave"
|
@save="editSave"
|
||||||
|
|
@ -468,6 +599,8 @@
|
||||||
|
|
||||||
<quantity-editor v-show="creating"
|
<quantity-editor v-show="creating"
|
||||||
v-model="newQuantity"
|
v-model="newQuantity"
|
||||||
|
:quantity-type="quantityType"
|
||||||
|
:material-types="materialTypes"
|
||||||
ref="newQuantity"
|
ref="newQuantity"
|
||||||
creating
|
creating
|
||||||
@save="createSave"
|
@save="createSave"
|
||||||
|
|
@ -484,6 +617,7 @@
|
||||||
name: String,
|
name: String,
|
||||||
value: Array,
|
value: Array,
|
||||||
quantityTypes: Array,
|
quantityTypes: Array,
|
||||||
|
materialTypes: Array,
|
||||||
defaultQuantityType: {
|
defaultQuantityType: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'standard',
|
default: 'standard',
|
||||||
|
|
@ -530,7 +664,7 @@
|
||||||
qty = Object.fromEntries(Object.entries(qty))
|
qty = Object.fromEntries(Object.entries(qty))
|
||||||
|
|
||||||
qty.uuid = 'new_' + this.newCounter++
|
qty.uuid = 'new_' + this.newCounter++
|
||||||
qty.as_text = "( " + this.measureMap[qty.measure] + " ) " + qty.value + " " + qty.units.name
|
qty.as_text = this.getQuantityText(qty)
|
||||||
|
|
||||||
const value = Array.from(this.value || [])
|
const value = Array.from(this.value || [])
|
||||||
value.push(qty)
|
value.push(qty)
|
||||||
|
|
@ -557,7 +691,7 @@
|
||||||
|
|
||||||
this.creating = true
|
this.creating = true
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$refs.newQuantity.focusMeasure()
|
this.$refs.newQuantity.focusForCreate()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -566,11 +700,25 @@
|
||||||
this.editing[qty.uuid] = true
|
this.editing[qty.uuid] = true
|
||||||
},
|
},
|
||||||
|
|
||||||
editSave(row) {
|
getQuantityText(qty) {
|
||||||
row.as_text = "( " + this.measureMap[row.measure] + " ) " + row.value + " " + row.units.name
|
let text = "( " + this.measureMap[qty.measure] + " ) "
|
||||||
|
+ qty.value + " " + qty.units.name
|
||||||
|
|
||||||
|
if (qty.quantity_type.drupal_id == 'material') {
|
||||||
|
const materials = qty.material_types.map((mtype) => {
|
||||||
|
return mtype.name
|
||||||
|
}).join(', ')
|
||||||
|
text = materials + " " + text
|
||||||
|
}
|
||||||
|
|
||||||
|
return text
|
||||||
|
},
|
||||||
|
|
||||||
|
editSave(qty) {
|
||||||
|
qty.as_text = this.getQuantityText(qty)
|
||||||
this.$emit('input', this.value)
|
this.$emit('input', this.value)
|
||||||
this.editing[row.uuid] = false
|
this.editing[qty.uuid] = false
|
||||||
this.$refs.table.closeDetailRow(row)
|
this.$refs.table.closeDetailRow(qty)
|
||||||
},
|
},
|
||||||
|
|
||||||
editCancel(qty) {
|
editCancel(qty) {
|
||||||
|
|
|
||||||
|
|
@ -78,4 +78,10 @@ def render_quantity_object(quantity):
|
||||||
measure = quantity["measure_name"]
|
measure = quantity["measure_name"]
|
||||||
value = quantity["value_decimal"]
|
value = quantity["value_decimal"]
|
||||||
unit = quantity["unit_name"]
|
unit = quantity["unit_name"]
|
||||||
return f"( {measure} ) {value} {unit}"
|
text = f"( {measure} ) {value} {unit}"
|
||||||
|
|
||||||
|
if quantity["quantity_type_id"] == "material":
|
||||||
|
materials = ", ".join([mtype["name"] for mtype in quantity["material_types"]])
|
||||||
|
return f"{materials} {text}"
|
||||||
|
|
||||||
|
return text
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ from wuttafarm.web.forms.schema import (
|
||||||
LogQuick,
|
LogQuick,
|
||||||
Notes,
|
Notes,
|
||||||
)
|
)
|
||||||
from wuttafarm.web.util import render_quantity_objects
|
from wuttafarm.web.util import render_quantity_objects, render_quantity_object
|
||||||
|
|
||||||
|
|
||||||
class LogMasterView(FarmOSMasterView):
|
class LogMasterView(FarmOSMasterView):
|
||||||
|
|
@ -199,7 +199,20 @@ class LogMasterView(FarmOSMasterView):
|
||||||
)
|
)
|
||||||
self.raw_json = result
|
self.raw_json = result
|
||||||
included = {obj["id"]: obj for obj in result.get("included", [])}
|
included = {obj["id"]: obj for obj in result.get("included", [])}
|
||||||
return self.normalize_log(result["data"], included)
|
instance = self.normalize_log(result["data"], included)
|
||||||
|
|
||||||
|
for qty in instance["quantities"]:
|
||||||
|
|
||||||
|
if qty["quantity_type_id"] == "material":
|
||||||
|
for mtype in qty["material_types"]:
|
||||||
|
result = self.farmos_client.resource.get_id(
|
||||||
|
"taxonomy_term", "material_type", mtype["uuid"]
|
||||||
|
)
|
||||||
|
mtype["name"] = result["data"]["attributes"]["name"]
|
||||||
|
|
||||||
|
qty["as_text"] = render_quantity_object(qty)
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
def get_instance_title(self, log):
|
def get_instance_title(self, log):
|
||||||
return log["name"]
|
return log["name"]
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ from wuttaweb.forms.schema import WuttaDateTime
|
||||||
from wuttaweb.forms.widgets import WuttaDateTimeWidget
|
from wuttaweb.forms.widgets import WuttaDateTimeWidget
|
||||||
|
|
||||||
from wuttafarm.web.views.farmos import FarmOSMasterView
|
from wuttafarm.web.views.farmos import FarmOSMasterView
|
||||||
from wuttafarm.web.forms.schema import FarmOSUnitRef
|
from wuttafarm.web.forms.schema import FarmOSUnitRef, FarmOSRefs
|
||||||
from wuttafarm.web.grids import ResourceData
|
from wuttafarm.web.grids import ResourceData
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -143,6 +143,7 @@ class QuantityMasterView(FarmOSMasterView):
|
||||||
sort_defaults = ("drupal_id", "desc")
|
sort_defaults = ("drupal_id", "desc")
|
||||||
|
|
||||||
form_fields = [
|
form_fields = [
|
||||||
|
"quantity_type_name",
|
||||||
"measure",
|
"measure",
|
||||||
"value",
|
"value",
|
||||||
"units",
|
"units",
|
||||||
|
|
@ -207,18 +208,23 @@ class QuantityMasterView(FarmOSMasterView):
|
||||||
def get_instance(self):
|
def get_instance(self):
|
||||||
# TODO: this pattern should be repeated for other views
|
# TODO: this pattern should be repeated for other views
|
||||||
try:
|
try:
|
||||||
quantity = self.farmos_client.resource.get_id(
|
result = self.farmos_client.resource.get_id(
|
||||||
"quantity", self.farmos_quantity_type, self.request.matchdict["uuid"]
|
"quantity",
|
||||||
|
self.farmos_quantity_type,
|
||||||
|
self.request.matchdict["uuid"],
|
||||||
|
params={"include": self.get_farmos_api_includes()},
|
||||||
)
|
)
|
||||||
except requests.HTTPError as exc:
|
except requests.HTTPError as exc:
|
||||||
if exc.response.status_code == 404:
|
if exc.response.status_code == 404:
|
||||||
raise self.notfound()
|
raise self.notfound()
|
||||||
|
|
||||||
self.raw_json = quantity
|
self.raw_json = result
|
||||||
|
|
||||||
data = self.normalize_quantity(quantity["data"])
|
included = {obj["id"]: obj for obj in result.get("included", [])}
|
||||||
|
assert included
|
||||||
|
data = self.normalize_quantity(result["data"], included)
|
||||||
|
|
||||||
if relationships := quantity["data"].get("relationships"):
|
if relationships := result["data"].get("relationships"):
|
||||||
|
|
||||||
# add units
|
# add units
|
||||||
if units := relationships.get("units"):
|
if units := relationships.get("units"):
|
||||||
|
|
@ -286,6 +292,11 @@ class QuantityMasterView(FarmOSMasterView):
|
||||||
f = form
|
f = form
|
||||||
super().configure_form(f)
|
super().configure_form(f)
|
||||||
|
|
||||||
|
# quantity_type_name
|
||||||
|
f.set_label("quantity_type_name", "Quantity Type")
|
||||||
|
f.set_readonly("quantity_type_name")
|
||||||
|
f.set_default("quantity_type_name", self.farmos_quantity_type.capitalize())
|
||||||
|
|
||||||
# created
|
# created
|
||||||
f.set_node("created", WuttaDateTime(self.request))
|
f.set_node("created", WuttaDateTime(self.request))
|
||||||
f.set_widget("created", WuttaDateTimeWidget(self.request))
|
f.set_widget("created", WuttaDateTimeWidget(self.request))
|
||||||
|
|
@ -338,6 +349,90 @@ class StandardQuantityView(QuantityMasterView):
|
||||||
return buttons
|
return buttons
|
||||||
|
|
||||||
|
|
||||||
|
class MaterialQuantityView(QuantityMasterView):
|
||||||
|
"""
|
||||||
|
View for farmOS Material Quantities
|
||||||
|
"""
|
||||||
|
|
||||||
|
model_name = "farmos_material_quantity"
|
||||||
|
model_title = "farmOS Material Quantity"
|
||||||
|
model_title_plural = "farmOS Material Quantities"
|
||||||
|
|
||||||
|
route_prefix = "farmos_quantities_material"
|
||||||
|
url_prefix = "/farmOS/quantities/material"
|
||||||
|
|
||||||
|
farmos_quantity_type = "material"
|
||||||
|
farmos_refurl_path = "/log-quantities/material"
|
||||||
|
|
||||||
|
def get_farmos_api_includes(self):
|
||||||
|
includes = super().get_farmos_api_includes()
|
||||||
|
includes.update({"material_type"})
|
||||||
|
return includes
|
||||||
|
|
||||||
|
def normalize_quantity(self, quantity, included={}):
|
||||||
|
normal = super().normalize_quantity(quantity, included)
|
||||||
|
|
||||||
|
material_type_objects = []
|
||||||
|
material_type_uuids = []
|
||||||
|
if relationships := quantity["relationships"]:
|
||||||
|
|
||||||
|
if material_types := relationships["material_type"]["data"]:
|
||||||
|
for mtype in material_types:
|
||||||
|
uuid = mtype["id"]
|
||||||
|
material_type_uuids.append(uuid)
|
||||||
|
material_type = {
|
||||||
|
"uuid": uuid,
|
||||||
|
"type": mtype["type"],
|
||||||
|
}
|
||||||
|
if mtype := included.get(uuid):
|
||||||
|
material_type.update(
|
||||||
|
{
|
||||||
|
"name": mtype["attributes"]["name"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
material_type_objects.append(material_type)
|
||||||
|
|
||||||
|
normal.update(
|
||||||
|
{
|
||||||
|
"material_types": material_type_objects,
|
||||||
|
"material_type_uuids": material_type_uuids,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return normal
|
||||||
|
|
||||||
|
def configure_form(self, form):
|
||||||
|
f = form
|
||||||
|
super().configure_form(f)
|
||||||
|
|
||||||
|
# material_types
|
||||||
|
f.fields.insert_before("measure", "material_types")
|
||||||
|
f.set_node("material_types", FarmOSRefs(self.request, "farmos_material_types"))
|
||||||
|
|
||||||
|
def get_xref_buttons(self, material_quantity):
|
||||||
|
model = self.app.model
|
||||||
|
session = self.Session()
|
||||||
|
buttons = []
|
||||||
|
|
||||||
|
if wf_material_quantity := (
|
||||||
|
session.query(model.MaterialQuantity)
|
||||||
|
.join(model.Quantity)
|
||||||
|
.filter(model.Quantity.farmos_uuid == material_quantity["uuid"])
|
||||||
|
.first()
|
||||||
|
):
|
||||||
|
buttons.append(
|
||||||
|
self.make_button(
|
||||||
|
f"View {self.app.get_title()} record",
|
||||||
|
primary=True,
|
||||||
|
url=self.request.route_url(
|
||||||
|
"quantities_material.view", uuid=wf_material_quantity.uuid
|
||||||
|
),
|
||||||
|
icon_left="eye",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return buttons
|
||||||
|
|
||||||
|
|
||||||
def defaults(config, **kwargs):
|
def defaults(config, **kwargs):
|
||||||
base = globals()
|
base = globals()
|
||||||
|
|
||||||
|
|
@ -349,6 +444,11 @@ def defaults(config, **kwargs):
|
||||||
)
|
)
|
||||||
StandardQuantityView.defaults(config)
|
StandardQuantityView.defaults(config)
|
||||||
|
|
||||||
|
MaterialQuantityView = kwargs.get(
|
||||||
|
"MaterialQuantityView", base["MaterialQuantityView"]
|
||||||
|
)
|
||||||
|
MaterialQuantityView.defaults(config)
|
||||||
|
|
||||||
|
|
||||||
def includeme(config):
|
def includeme(config):
|
||||||
defaults(config)
|
defaults(config)
|
||||||
|
|
|
||||||
|
|
@ -290,7 +290,9 @@ class LogMasterView(WuttaFarmMasterView):
|
||||||
f.set_node("quantities", QuantityRefs(self.request))
|
f.set_node("quantities", QuantityRefs(self.request))
|
||||||
if not self.creating:
|
if not self.creating:
|
||||||
# nb. must explicity declare value for non-standard field
|
# nb. must explicity declare value for non-standard field
|
||||||
f.set_default("quantities", log.quantities)
|
f.set_default(
|
||||||
|
"quantities", [self.app.get_true_quantity(q) for q in log.quantities]
|
||||||
|
)
|
||||||
|
|
||||||
# notes
|
# notes
|
||||||
f.set_widget("notes", "notes")
|
f.set_widget("notes", "notes")
|
||||||
|
|
@ -389,15 +391,15 @@ class LogMasterView(WuttaFarmMasterView):
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
session = self.Session()
|
session = self.Session()
|
||||||
|
|
||||||
current = {qty.uuid.hex: qty for qty in log.quantities}
|
current = {
|
||||||
|
qty.uuid.hex: self.app.get_true_quantity(qty) for qty in log.quantities
|
||||||
|
}
|
||||||
for new_qty in desired:
|
for new_qty in desired:
|
||||||
units = session.get(model.Unit, new_qty["units"]["uuid"])
|
units = session.get(model.Unit, new_qty["units"]["uuid"])
|
||||||
assert units
|
assert units
|
||||||
if new_qty["uuid"].startswith("new_"):
|
if new_qty["uuid"].startswith("new_"):
|
||||||
assert new_qty["quantity_type"]["drupal_id"] == "standard"
|
qty = self.app.make_true_quantity(
|
||||||
factory = model.StandardQuantity
|
new_qty["quantity_type"]["drupal_id"],
|
||||||
qty = factory(
|
|
||||||
quantity_type_id=new_qty["quantity_type"]["drupal_id"],
|
|
||||||
measure_id=new_qty["measure"],
|
measure_id=new_qty["measure"],
|
||||||
value_numerator=int(new_qty["value"]),
|
value_numerator=int(new_qty["value"]),
|
||||||
value_denominator=1,
|
value_denominator=1,
|
||||||
|
|
@ -413,6 +415,8 @@ class LogMasterView(WuttaFarmMasterView):
|
||||||
old_qty.value_numerator = int(new_qty["value"])
|
old_qty.value_numerator = int(new_qty["value"])
|
||||||
old_qty.value_denominator = 1
|
old_qty.value_denominator = 1
|
||||||
old_qty.units = units
|
old_qty.units = units
|
||||||
|
if old_qty.quantity_type_id == "material":
|
||||||
|
self.set_material_types(old_qty, new_qty["material_types"])
|
||||||
|
|
||||||
desired = [qty["uuid"] for qty in desired]
|
desired = [qty["uuid"] for qty in desired]
|
||||||
for old_qty in list(log.quantities):
|
for old_qty in list(log.quantities):
|
||||||
|
|
@ -421,6 +425,22 @@ class LogMasterView(WuttaFarmMasterView):
|
||||||
if old_qty.uuid and old_qty.uuid.hex not in desired:
|
if old_qty.uuid and old_qty.uuid.hex not in desired:
|
||||||
log.quantities.remove(old_qty)
|
log.quantities.remove(old_qty)
|
||||||
|
|
||||||
|
def set_material_types(self, quantity, desired):
|
||||||
|
model = self.app.model
|
||||||
|
session = self.Session()
|
||||||
|
current = {mtype.uuid: mtype for mtype in quantity.material_types}
|
||||||
|
|
||||||
|
for new_mtype in desired:
|
||||||
|
mtype = session.get(model.MaterialType, new_mtype["uuid"])
|
||||||
|
assert mtype
|
||||||
|
if mtype.uuid not in current:
|
||||||
|
quantity.material_types.append(mtype)
|
||||||
|
|
||||||
|
desired = [mtype["uuid"] for mtype in desired]
|
||||||
|
for old_mtype in current.values():
|
||||||
|
if old_mtype.uuid.hex not in desired:
|
||||||
|
quantity.material_types.remove(old_mtype)
|
||||||
|
|
||||||
def auto_sync_to_farmos(self, client, log):
|
def auto_sync_to_farmos(self, client, log):
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
session = self.Session()
|
session = self.Session()
|
||||||
|
|
@ -429,11 +449,8 @@ class LogMasterView(WuttaFarmMasterView):
|
||||||
session.flush()
|
session.flush()
|
||||||
|
|
||||||
for qty in log.quantities:
|
for qty in log.quantities:
|
||||||
# TODO: support more quantity types
|
qty = self.app.get_true_quantity(qty)
|
||||||
if qty.quantity_type_id == "standard":
|
self.app.auto_sync_to_farmos(qty, client=client)
|
||||||
qty = session.get(model.StandardQuantity, qty.uuid)
|
|
||||||
assert qty
|
|
||||||
self.app.auto_sync_to_farmos(qty, client=client)
|
|
||||||
|
|
||||||
self.app.auto_sync_to_farmos(log, client=client)
|
self.app.auto_sync_to_farmos(log, client=client)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,13 @@ from webhelpers2.html import tags
|
||||||
from wuttaweb.db import Session
|
from wuttaweb.db import Session
|
||||||
|
|
||||||
from wuttafarm.web.views import WuttaFarmMasterView
|
from wuttafarm.web.views import WuttaFarmMasterView
|
||||||
from wuttafarm.db.model import QuantityType, Quantity, StandardQuantity
|
from wuttafarm.db.model import (
|
||||||
from wuttafarm.web.forms.schema import UnitRef, LogRef
|
QuantityType,
|
||||||
|
Quantity,
|
||||||
|
StandardQuantity,
|
||||||
|
MaterialQuantity,
|
||||||
|
)
|
||||||
|
from wuttafarm.web.forms.schema import UnitRef, LogRef, MaterialTypeRefs
|
||||||
from wuttafarm.util import get_log_type_enum
|
from wuttafarm.util import get_log_type_enum
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -370,6 +375,74 @@ class StandardQuantityView(QuantityMasterView):
|
||||||
farmos_refurl_path = "/log-quantities/standard"
|
farmos_refurl_path = "/log-quantities/standard"
|
||||||
|
|
||||||
|
|
||||||
|
class MaterialQuantityView(QuantityMasterView):
|
||||||
|
"""
|
||||||
|
Master view for Material Quantities
|
||||||
|
"""
|
||||||
|
|
||||||
|
model_class = MaterialQuantity
|
||||||
|
route_prefix = "quantities_material"
|
||||||
|
url_prefix = "/quantities/material"
|
||||||
|
|
||||||
|
farmos_bundle = "material"
|
||||||
|
farmos_refurl_path = "/log-quantities/material"
|
||||||
|
|
||||||
|
def configure_grid(self, grid):
|
||||||
|
g = grid
|
||||||
|
super().configure_grid(g)
|
||||||
|
|
||||||
|
# material_types
|
||||||
|
g.columns.append("material_types")
|
||||||
|
g.set_label("material_types", "Material Type", column_only=True)
|
||||||
|
g.set_renderer("material_types", self.render_material_types_for_grid)
|
||||||
|
|
||||||
|
def render_material_types_for_grid(self, quantity, field, value):
|
||||||
|
if self.farmos_style_grid_links:
|
||||||
|
links = []
|
||||||
|
for mtype in quantity.material_types:
|
||||||
|
url = self.request.route_url("material_types.view", uuid=mtype.uuid)
|
||||||
|
links.append(tags.link_to(str(mtype), url))
|
||||||
|
return ", ".join(links)
|
||||||
|
|
||||||
|
return ", ".join([str(mtype) for mtype in quantity.material_types])
|
||||||
|
|
||||||
|
def configure_form(self, form):
|
||||||
|
f = form
|
||||||
|
super().configure_form(f)
|
||||||
|
quantity = form.model_instance
|
||||||
|
|
||||||
|
# material_types
|
||||||
|
f.fields.insert_after("quantity_type", "material_types")
|
||||||
|
f.set_node("material_types", MaterialTypeRefs(self.request))
|
||||||
|
if not self.creating:
|
||||||
|
f.set_default("material_types", quantity.material_types)
|
||||||
|
|
||||||
|
def objectify(self, form):
|
||||||
|
quantity = super().objectify(form)
|
||||||
|
data = form.validated
|
||||||
|
|
||||||
|
self.set_material_types(quantity, data["material_types"])
|
||||||
|
|
||||||
|
return quantity
|
||||||
|
|
||||||
|
def set_material_types(self, quantity, desired):
|
||||||
|
model = self.app.model
|
||||||
|
session = self.Session()
|
||||||
|
|
||||||
|
current = {mt.uuid.hex: mt for mt in quantity.material_types}
|
||||||
|
|
||||||
|
for mtype in desired:
|
||||||
|
if mtype["uuid"] not in current:
|
||||||
|
mtype = session.get(model.MaterialType, mtype["uuid"])
|
||||||
|
assert mtype
|
||||||
|
quantity.material_types.append(mtype)
|
||||||
|
|
||||||
|
desired = [mtype["uuid"] for mtype in desired]
|
||||||
|
for uuid, mtype in current.items():
|
||||||
|
if uuid not in desired:
|
||||||
|
quantity.material_types.remove(mtype)
|
||||||
|
|
||||||
|
|
||||||
def defaults(config, **kwargs):
|
def defaults(config, **kwargs):
|
||||||
base = globals()
|
base = globals()
|
||||||
|
|
||||||
|
|
@ -384,6 +457,11 @@ def defaults(config, **kwargs):
|
||||||
)
|
)
|
||||||
StandardQuantityView.defaults(config)
|
StandardQuantityView.defaults(config)
|
||||||
|
|
||||||
|
MaterialQuantityView = kwargs.get(
|
||||||
|
"MaterialQuantityView", base["MaterialQuantityView"]
|
||||||
|
)
|
||||||
|
MaterialQuantityView.defaults(config)
|
||||||
|
|
||||||
|
|
||||||
def includeme(config):
|
def includeme(config):
|
||||||
defaults(config)
|
defaults(config)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue