Adding on_website and preferred part indicators
This commit is contained in:
1 parent
13b64a2d17
commit
4fd2bb1b58
13 files changed
+331
-6
No files matched your search
@@ -59,6 +59,8 @@ async def create_augmented_part(
|
||||
scope_model_id=payload.scope_model_id,
|
||||
part_type_id=payload.part_type_id,
|
||||
sub_part_type_id=payload.sub_part_type_id,
|
||||
on_website=payload.on_website,
|
||||
preferred_part=payload.preferred_part,
|
||||
)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
|
||||
@@ -10,7 +10,14 @@ from app.models.part_type import PartType
|
||||
from app.models.scope_model import ScopeModel
|
||||
from app.models.sub_part_type import SubPartType
|
||||
|
||||
_RELATIONSHIPS = ["colour", "manufacturer", "scope_model", "part_type", "sub_part_type"]
|
||||
_RELATIONSHIPS = [
|
||||
"colour",
|
||||
"manufacturer",
|
||||
"scope_model",
|
||||
"part_type",
|
||||
"sub_part_type",
|
||||
"notes",
|
||||
]
|
||||
|
||||
# Sortable columns for the admin list, keyed by the query-string "sort" value.
|
||||
# Columns backed by a related table's name require an outer join (the FK may be
|
||||
@@ -87,6 +94,7 @@ async def list_filtered(
|
||||
part_type_id: int | None = None,
|
||||
sub_part_type_id: int | None = None,
|
||||
include_archived: bool = False,
|
||||
on_website: bool = False,
|
||||
sort: str = "description",
|
||||
direction: str = "asc",
|
||||
skip: int = 0,
|
||||
@@ -110,6 +118,8 @@ async def list_filtered(
|
||||
q = q.where(AugmentedPart.sub_part_type_id == sub_part_type_id)
|
||||
if not include_archived:
|
||||
q = q.where(AugmentedPart.archived.is_(False))
|
||||
if on_website:
|
||||
q = q.where(AugmentedPart.on_website.is_(True))
|
||||
q = _apply_sort(q, sort, direction)
|
||||
result = await db.execute(q.offset(skip).limit(limit))
|
||||
return list(result.scalars().all())
|
||||
@@ -124,6 +134,7 @@ async def count_filtered(
|
||||
part_type_id: int | None = None,
|
||||
sub_part_type_id: int | None = None,
|
||||
include_archived: bool = False,
|
||||
on_website: bool = False,
|
||||
) -> int:
|
||||
from sqlalchemy import func
|
||||
|
||||
@@ -145,6 +156,8 @@ async def count_filtered(
|
||||
q = q.where(AugmentedPart.sub_part_type_id == sub_part_type_id)
|
||||
if not include_archived:
|
||||
q = q.where(AugmentedPart.archived.is_(False))
|
||||
if on_website:
|
||||
q = q.where(AugmentedPart.on_website.is_(True))
|
||||
result = await db.execute(q)
|
||||
return result.scalar_one()
|
||||
|
||||
@@ -220,6 +233,8 @@ async def create(
|
||||
scope_model_id: int | None,
|
||||
part_type_id: int,
|
||||
sub_part_type_id: int | None,
|
||||
on_website: bool = False,
|
||||
preferred_part: bool = False,
|
||||
) -> AugmentedPart:
|
||||
ap = AugmentedPart(
|
||||
sku=sku,
|
||||
@@ -229,6 +244,8 @@ async def create(
|
||||
scope_model_id=scope_model_id,
|
||||
part_type_id=part_type_id,
|
||||
sub_part_type_id=sub_part_type_id,
|
||||
on_website=on_website,
|
||||
preferred_part=preferred_part,
|
||||
)
|
||||
db.add(ap)
|
||||
await db.commit()
|
||||
|
||||
@@ -47,6 +47,8 @@ class AugmentedPart(Base):
|
||||
# removed, so fit/attachment-point history is preserved. Hidden from list
|
||||
# views by default.
|
||||
archived: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||
on_website: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||
preferred_part: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
|
||||
|
||||
# linked imported parts — not eagerly loaded; used by sync service only
|
||||
imported_parts: Mapped[list[ImportedPart]] = relationship(
|
||||
|
||||
@@ -39,6 +39,8 @@ class AugmentedPartBase(BaseModel):
|
||||
scope_model_id: int | None = None
|
||||
part_type_id: int
|
||||
sub_part_type_id: int | None = None
|
||||
on_website: bool = False
|
||||
preferred_part: bool = False
|
||||
|
||||
|
||||
class AugmentedPartCreate(AugmentedPartBase):
|
||||
@@ -54,6 +56,8 @@ class AugmentedPartUpdate(BaseModel):
|
||||
part_type_id: int | None = None
|
||||
sub_part_type_id: int | None = None
|
||||
archived: bool | None = None
|
||||
on_website: bool | None = None
|
||||
preferred_part: bool | None = None
|
||||
|
||||
|
||||
class AugmentedPartSyncResult(BaseModel):
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
<td>{{ ap.part_type.name }}</td>
|
||||
<td>{{ ap.sub_part_type.name if ap.sub_part_type else '—' }}</td>
|
||||
<td>{% if ap.archived %}<span class="tag">Archived</span>{% else %}—{% endif %}</td>
|
||||
<td>{% if ap.on_website %}<span class="tag">Yes</span>{% else %}—{% endif %}</td>
|
||||
<td>{% if ap.preferred_part %}<span class="tag">Yes</span>{% else %}—{% endif %}</td>
|
||||
<td class="row-actions">
|
||||
<button class="btn btn-ghost"
|
||||
hx-get="/admin/augmented-parts/{{ ap.id }}/edit"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<tr id="augmented-part-{{ ap.id }}">
|
||||
<td colspan="10">
|
||||
<td colspan="12">
|
||||
<form id="augmented-part-edit-form-{{ ap.id }}"
|
||||
hx-post="/admin/augmented-parts/{{ ap.id }}"
|
||||
hx-target="#augmented-part-{{ ap.id }}"
|
||||
@@ -45,6 +45,8 @@
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<label class="check"><input type="checkbox" name="on_website" value="true" {{ "checked" if ap.on_website }}> On website</label>
|
||||
<label class="check"><input type="checkbox" name="preferred_part" value="true" {{ "checked" if ap.preferred_part }}> Preferred part</label>
|
||||
<div class="row-actions">
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
<button type="button" class="btn btn-ghost"
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label><input type="checkbox" name="include_archived" {{ "checked" if include_archived }}> Show archived</label>
|
||||
<label><input type="checkbox" name="on_website" {{ "checked" if on_website }}> On website</label>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
{{ sort_th("part_type", "Type", sort_headers) }}
|
||||
{{ sort_th("sub_part_type", "Sub Type", sort_headers) }}
|
||||
<th>Archived</th>
|
||||
<th>On Website</th>
|
||||
<th>Preferred</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -20,7 +22,7 @@
|
||||
{% for ap in augmented_parts %}
|
||||
{% include "admin/_augmented_part_row.html" %}
|
||||
{% else %}
|
||||
<tr><td colspan="10" class="muted">No augmented parts recorded.</td></tr>
|
||||
<tr><td colspan="12" class="muted">No augmented parts recorded.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
<span class="tag">{{ ap.part_type.name }}</span>
|
||||
{% if ap.sub_part_type %}<span class="tag">{{ ap.sub_part_type.name }}</span>{% endif %}
|
||||
{% if ap.archived %}<span class="tag">Archived</span>{% endif %}
|
||||
{% if ap.on_website %}<span class="tag">On website</span>{% endif %}
|
||||
{% if ap.preferred_part %}<span class="tag">Preferred</span>{% endif %}
|
||||
{% if ap.description %}{{ ap.description }}{% endif %}
|
||||
</p>
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<label class="check"><input type="checkbox" name="on_website" value="true"> On website</label>
|
||||
<label class="check"><input type="checkbox" name="preferred_part" value="true"> Preferred part</label>
|
||||
<button type="submit" class="btn btn-primary">Add</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
+25
-3
@@ -1674,6 +1674,7 @@ async def _default_augmented_parts_table_ctx(db: AsyncSession) -> dict:
|
||||
"sort": "description",
|
||||
"dir": "asc",
|
||||
"include_archived": False,
|
||||
"on_website": False,
|
||||
}
|
||||
|
||||
|
||||
@@ -1683,23 +1684,30 @@ async def augmented_parts_page(
|
||||
sort: str = "description",
|
||||
dir: str = "asc",
|
||||
include_archived: bool = False,
|
||||
on_website: bool = False,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
user: User = Depends(web_require("augmented_parts:read")),
|
||||
):
|
||||
ctx = await _augmented_part_form_ctx(db)
|
||||
total = await augmented_part_crud.count_filtered(
|
||||
db, include_archived=include_archived
|
||||
db, include_archived=include_archived, on_website=on_website
|
||||
)
|
||||
ctx["augmented_parts"] = await augmented_part_crud.list_filtered(
|
||||
db,
|
||||
sort=sort,
|
||||
direction=dir,
|
||||
include_archived=include_archived,
|
||||
on_website=on_website,
|
||||
limit=_PAGE_SIZE,
|
||||
)
|
||||
ctx["pagination"] = _paginate(
|
||||
"/admin/augmented-parts/filter",
|
||||
{"sort": sort, "dir": dir, "include_archived": include_archived},
|
||||
{
|
||||
"sort": sort,
|
||||
"dir": dir,
|
||||
"include_archived": include_archived,
|
||||
"on_website": on_website,
|
||||
},
|
||||
1,
|
||||
_PAGE_SIZE,
|
||||
total,
|
||||
@@ -1707,7 +1715,7 @@ async def augmented_parts_page(
|
||||
)
|
||||
ctx["sort_headers"] = _sort_headers(
|
||||
"/admin/augmented-parts/filter",
|
||||
{"include_archived": include_archived},
|
||||
{"include_archived": include_archived, "on_website": on_website},
|
||||
_AUGMENTED_PARTS_SORT_COLUMNS,
|
||||
sort,
|
||||
dir,
|
||||
@@ -1716,6 +1724,7 @@ async def augmented_parts_page(
|
||||
ctx["sort"] = sort
|
||||
ctx["dir"] = dir
|
||||
ctx["include_archived"] = include_archived
|
||||
ctx["on_website"] = on_website
|
||||
ctx["user"] = user
|
||||
return templates.TemplateResponse(request, "admin/augmented_parts.html", ctx)
|
||||
|
||||
@@ -1730,6 +1739,8 @@ async def create_augmented_part(
|
||||
scope_model_id: str = Form(""),
|
||||
part_type_id: int = Form(...),
|
||||
sub_part_type_id: str = Form(""),
|
||||
on_website: bool = Form(False),
|
||||
preferred_part: bool = Form(False),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
actor: User = Depends(web_require("augmented_parts:write")),
|
||||
):
|
||||
@@ -1745,6 +1756,8 @@ async def create_augmented_part(
|
||||
scope_model_id=int(scope_model_id) if scope_model_id else None,
|
||||
part_type_id=part_type_id,
|
||||
sub_part_type_id=int(sub_part_type_id) if sub_part_type_id else None,
|
||||
on_website=on_website,
|
||||
preferred_part=preferred_part,
|
||||
)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
@@ -1874,6 +1887,7 @@ async def filter_augmented_parts(
|
||||
part_type_id: str = "",
|
||||
sub_part_type_id: str = "",
|
||||
include_archived: bool = False,
|
||||
on_website: bool = False,
|
||||
sort: str = "description",
|
||||
dir: str = "asc",
|
||||
page: int = 1,
|
||||
@@ -1895,6 +1909,7 @@ async def filter_augmented_parts(
|
||||
part_type_id=part_type_id_int,
|
||||
sub_part_type_id=sub_part_type_id_int,
|
||||
include_archived=include_archived,
|
||||
on_website=on_website,
|
||||
)
|
||||
filter_params = {
|
||||
"description": description,
|
||||
@@ -1904,6 +1919,7 @@ async def filter_augmented_parts(
|
||||
"part_type_id": part_type_id,
|
||||
"sub_part_type_id": sub_part_type_id,
|
||||
"include_archived": include_archived,
|
||||
"on_website": on_website,
|
||||
}
|
||||
pagination = _paginate(
|
||||
"/admin/augmented-parts/filter",
|
||||
@@ -1922,6 +1938,7 @@ async def filter_augmented_parts(
|
||||
part_type_id=part_type_id_int,
|
||||
sub_part_type_id=sub_part_type_id_int,
|
||||
include_archived=include_archived,
|
||||
on_website=on_website,
|
||||
sort=sort,
|
||||
direction=dir,
|
||||
skip=pagination["skip"],
|
||||
@@ -1945,6 +1962,7 @@ async def filter_augmented_parts(
|
||||
"sort": sort,
|
||||
"dir": dir,
|
||||
"include_archived": include_archived,
|
||||
"on_website": on_website,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -2188,6 +2206,8 @@ async def update_augmented_part(
|
||||
scope_model_id: str = Form(""),
|
||||
part_type_id: int = Form(...),
|
||||
sub_part_type_id: str = Form(""),
|
||||
on_website: bool = Form(False),
|
||||
preferred_part: bool = Form(False),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
actor: User = Depends(web_require("augmented_parts:write")),
|
||||
):
|
||||
@@ -2207,6 +2227,8 @@ async def update_augmented_part(
|
||||
"scope_model_id": int(scope_model_id) if scope_model_id else None,
|
||||
"part_type_id": part_type_id,
|
||||
"sub_part_type_id": int(sub_part_type_id) if sub_part_type_id else None,
|
||||
"on_website": on_website,
|
||||
"preferred_part": preferred_part,
|
||||
}
|
||||
await augmented_part_crud.update(db, ap, changes)
|
||||
await audit_crud.log(
|
||||
|
||||
Reference in new issue
Block a user