diff --git a/inventory/routes/entry.py b/inventory/routes/entry.py
index 951d56e..2f8ab7d 100644
--- a/inventory/routes/entry.py
+++ b/inventory/routes/entry.py
@@ -81,7 +81,11 @@ def _fields_for_model(model: str):
"title",
"active",
"staff",
- "supervisor.id"
+ "supervisor.id",
+ "inventory.label",
+ "inventory.brand.name",
+ "inventory.model",
+ "inventory.device_type.description"
]
fields_spec = [
{"name": "label", "row": "label", "label": "", "type": "display",
@@ -106,6 +110,7 @@ def _fields_for_model(model: str):
"row": "checkboxes", "attrs": {"class": "form-check-input"}, "wrap": {"class": "form-check"}},
{"name": "staff", "label": "Staff Member", "label_attrs": {"class": "form-check-label"},
"row": "checkboxes", "attrs": {"class": "form-check-input"}, "wrap": {"class": "form-check"}},
+ {"name": "inventory", "label": "Inventory", "type": "template", "row": "inventory", "template": "user_inventory.html"},
]
layout = [
{"name": "label", "order": 0, "attrs": {"class": "row align-items-center"}},
@@ -113,6 +118,7 @@ def _fields_for_model(model: str):
{"name": "details", "order": 20, "attrs": {"class": "row mt-2"}},
{"name": "checkboxes", "order": 30, "parent": "details",
"attrs": {"class": "col d-flex flex-column justify-content-end"}},
+ {"name": "inventory", "order": 40},
]
elif model == "worklog":
@@ -127,7 +133,7 @@ def _fields_for_model(model: str):
"updates.id",
"updates.content",
"updates.timestamp",
- "updates.is_deleted",
+ "updates.is_deleted"
]
fields_spec = [
{"name": "id", "label": "", "type": "display", "label_spec": "Work Item #{id}",
diff --git a/inventory/routes/index.py b/inventory/routes/index.py
index d0ee22b..240089b 100644
--- a/inventory/routes/index.py
+++ b/inventory/routes/index.py
@@ -16,6 +16,7 @@ bp_index = Blueprint("index", __name__)
def init_index_routes(app):
@bp_index.get("/")
def index():
+ # 1. work log stuff (leave it)
work_log_service = crudkit.crud.get_service(WorkLog)
work_logs = work_log_service.list({
"complete__ne": 1,
@@ -33,36 +34,69 @@ def init_index_routes(app):
{"field": "work_item.label", "label": "Work Item",
"link": {"endpoint": "entry.entry", "params": {"id": "{work_item.id}", "model": "inventory"}}}
]
-
logs = render_table(work_logs, columns=columns, opts={"object_class": "worklog"})
+ # 2. get device types with targets
device_type_service = crudkit.crud.get_service(DeviceType)
- device_types = device_type_service.list({
+ dt_rows = device_type_service.list({
'limit': 0,
'target__gt': 0,
'fields': [
'description',
- 'target',
- 'condition.category'
+ 'target'
],
+ "sort": "description",
})
- device_types = [d.as_dict() for d in device_types]
- dt_ids = [d['id'] for d in device_types]
- dt_filter = {'$or': [
- {'device_type_id': d} for d in dt_ids
- ],
- 'condition.category': 'Available'}
+ # turn into df
+ device_types = pd.DataFrame([d.as_dict() for d in dt_rows])
+
+ # if nobody has targets, just show empty table
+ if device_types.empty:
+ empty_df = pd.DataFrame(columns=['id', 'description', 'target', 'actual', 'needed'])
+ return render_template("index.html", logs=logs, needed_inventory=empty_df)
+
+ # 3. now we can safely collect ids from the DF
+ dt_ids = device_types['id'].tolist()
+
+ # 4. build inventory filter
+ dt_filter = {
+ '$or': [{'device_type_id': d} for d in dt_ids],
+ # drop this if you decided to ignore condition
+ 'condition.category': 'Available'
+ }
+
+ # 5. fetch inventory
inventory_service = crudkit.crud.get_service(Inventory)
- needed_inventory = inventory_service.list({
+ inv_rows = inventory_service.list({
'limit': 0,
**dt_filter,
- 'fields': ['device_type.description']
+ 'fields': ['device_type.description'],
+ 'sort': 'device_type.description',
})
- needed_inventory = pd.DataFrame([i.as_dict() for i in needed_inventory])
- needed_inventory = pd.pivot_table(needed_inventory, columns='device_type.description', aggfunc='size')
+ inventory_df = pd.DataFrame([i.as_dict() for i in inv_rows])
- return render_template("index.html", logs=logs, device_types=device_types, needed_inventory=needed_inventory)
+ # if there is no inventory for these device types, actual = 0
+ if inventory_df.empty:
+ device_types['actual'] = 0
+ device_types['needed'] = device_types['target']
+ return render_template("index.html", logs=logs, needed_inventory=device_types)
+
+ # 6. aggregate counts
+ inv_counts = (
+ inventory_df['device_type.description']
+ .value_counts()
+ .rename('actual')
+ .reset_index()
+ .rename(columns={'device_type.description': 'description'})
+ )
+
+ # 7. merge
+ merged = device_types.merge(inv_counts, on='description', how='left')
+ merged['actual'] = merged['actual'].fillna(0).astype(int)
+ merged['needed'] = (merged['target'] - merged['actual']).clip(lower=0)
+
+ return render_template("index.html", logs=logs, needed_inventory=merged)
@bp_index.get("/LICENSE")
def license():
diff --git a/inventory/routes/settings.py b/inventory/routes/settings.py
index 1128422..4885d1b 100644
--- a/inventory/routes/settings.py
+++ b/inventory/routes/settings.py
@@ -53,7 +53,6 @@ def init_settings_routes(app):
],
})
statuses = render_table(statuses, opts={"object_class": 'status'})
- print([t.as_dict() for t in device_types])
return render_template("settings.html", brands=brands, device_types=device_types, areas=areas, functions=functions, rooms=rooms, statuses=statuses)
diff --git a/inventory/templates/index.html b/inventory/templates/index.html
index 7ef4a05..eaaf426 100644
--- a/inventory/templates/index.html
+++ b/inventory/templates/index.html
@@ -11,14 +11,38 @@
Supply Status
- {% for d in device_types %}
-
- {{ d['description'] }}: {{ d['target'] }} needed
-
- {% endfor %}
-
- {{ needed_inventory }}
-
+
+
+ {% if not needed_inventory.empty %}
+
+
+ | Device |
+ Target |
+ On Hand |
+ Needed |
+
+
+
+ {% for row in needed_inventory.itertuples() %}
+
+ | {{ row.description }} |
+ {{ row.target }} |
+ {{ row.actual }} |
+ {{ row.needed }} |
+
+ {% endfor %}
+
+ {% else %}
+
+
+ | No data. |
+
+
+ {% endif %}
+
+
-{% endblock %}
+{% endblock %}
\ No newline at end of file
diff --git a/inventory/templates/summary.html b/inventory/templates/summary.html
index f407356..e7833ab 100644
--- a/inventory/templates/summary.html
+++ b/inventory/templates/summary.html
@@ -1,48 +1,54 @@
{% extends "base.html" %}
{% block style %}
- thead.sticky-top th {
- z-index: 2;
- }
+thead.sticky-top th {
+z-index: 2;
+}
{% endblock %}
{% block main %}
- Inventory Summary
-
-
-
-
- | Device Type |
- {% for col in col_headers %}
- {% if col.href %}
- {{ col.label }} |
- {% else %}
- {{ col.label }} |
- {% endif %}
- {% endfor %}
-
-
-
- {% for row in table_rows %}
- {% set need_more = (row['cells'][-2]['value'] | int > 0) %}
-
- |
- {% if row.href %}
- {{ row.label }}
- {% else %}
- {{ row.label }}
- {% endif %}
- |
- {% for cell in row.cells %}
- {% if cell.href %}
- {{ cell.value }} |
- {% else %}
- {{ cell.value }} |
- {% endif %}
- {% endfor %}
-
+Inventory Summary
+
+
+
+
+ | Device Type |
+ {% for col in col_headers %}
+ {% if col.href %}
+ {{ col.label }} |
+ {% else %}
+ {{ col.label }} |
+ {% endif %}
{% endfor %}
-
-
-
-{% endblock %}
+
+
+
+ {% for row in table_rows %}
+ {% set need_more = (row['cells'][-2]['value'] | int > 0) %}
+
+ |
+ {% if row.href %}
+ {{ row.label }}
+ {% else %}
+ {{ row.label }}
+ {% endif %}
+ |
+ {% for cell in row.cells %}
+ {% if cell.href %}
+
+ {{ cell.value }} |
+ {% else %}
+
+ {{ cell.value }} |
+ {% endif %}
+ {% endfor %}
+
+ {% endfor %}
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/inventory/templates/user_inventory.html b/inventory/templates/user_inventory.html
new file mode 100644
index 0000000..b2d3c40
--- /dev/null
+++ b/inventory/templates/user_inventory.html
@@ -0,0 +1,33 @@
+
+{% set inv = field['template_ctx']['values']['inventory'] %}
+
+
+
+ {% if inv %}
+
+
+ | Device |
+ Brand |
+ Model |
+ Type |
+
+
+
+ {% for i in inv %}
+
+ | {{ i.label }} |
+ {{ i['brand.name'] }} |
+ {{ i.model }} |
+ {{ i['device_type.description'] }} |
+
+ {% endfor %}
+
+ {% else %}
+
+
+ | No data. |
+
+
+ {% endif %}
+
+
\ No newline at end of file