Creation logic!

This commit is contained in:
Yaro Kasear 2025-10-03 13:43:23 -05:00
parent 2502375d32
commit 21a3399ecd
6 changed files with 45 additions and 10 deletions

View file

@ -653,9 +653,13 @@ class CRUDService(Generic[T]):
session = self.session session = self.session
obj = self.model(**data) obj = self.model(**data)
session.add(obj) session.add(obj)
session.flush()
self._log_version("create", obj, actor, commit=commit)
if commit: if commit:
session.commit() session.commit()
self._log_version("create", obj, actor, commit=commit)
return obj return obj
def update(self, id: int, data: dict, actor=None, *, commit: bool = True) -> T: def update(self, id: int, data: dict, actor=None, *, commit: bool = True) -> T:

View file

@ -2,7 +2,7 @@ from typing import List, Optional
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Unicode, case, cast, func from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Unicode, case, cast, func
from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.orm import Mapped, mapped_column, relationship, synonym
from sqlalchemy.sql import expression as sql from sqlalchemy.sql import expression as sql
from crudkit.core.base import Base, CRUDMixin from crudkit.core.base import Base, CRUDMixin
@ -21,13 +21,14 @@ class Inventory(Base, CRUDMixin):
model: Mapped[Optional[str]] = mapped_column(Unicode(255)) model: Mapped[Optional[str]] = mapped_column(Unicode(255))
notes: Mapped[Optional[str]] = mapped_column(Unicode(255)) notes: Mapped[Optional[str]] = mapped_column(Unicode(255))
shared: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=sql.false()) shared: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=sql.false())
timestamp: Mapped[DateTime] = mapped_column(DateTime) timestamp: Mapped[DateTime] = mapped_column(DateTime, default=func.now(), nullable=False)
brand: Mapped[Optional['Brand']] = relationship('Brand', back_populates='inventory') brand: Mapped[Optional['Brand']] = relationship('Brand', back_populates='inventory')
brand_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey('brand.id'), nullable=True, index=True) brand_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey('brand.id'), nullable=True, index=True)
device_type: Mapped[Optional['DeviceType']] = relationship('DeviceType', back_populates='inventory') device_type: Mapped[Optional['DeviceType']] = relationship('DeviceType', back_populates='inventory')
type_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("item.id"), nullable=True, index=True) type_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("item.id"), nullable=True, index=True)
device_type_id = synonym("type_id")
image: Mapped[Optional['Image']] = relationship('Image', back_populates='inventory', passive_deletes=True) image: Mapped[Optional['Image']] = relationship('Image', back_populates='inventory', passive_deletes=True)
image_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey('images.id', ondelete='SET NULL'), nullable=True, index=True) image_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey('images.id', ondelete='SET NULL'), nullable=True, index=True)

View file

@ -240,6 +240,14 @@ def init_entry_routes(app):
updates = payload.pop("updates", []) or [] updates = payload.pop("updates", []) or []
payload.pop("delete_update_ids", None) # irrelevant on create payload.pop("delete_update_ids", None) # irrelevant on create
if model == "inventory":
if "device_type_id" in payload and "type_id" not in payload:
payload["type_id"] = payload.pop("device_type_id")
for k in ("brand_id", "type_id", "owner_id", "location_id", "image_id"):
if payload.get(k) == "":
payload[k] = None
# payload["timestamp"] = datetime.now()
if model == "worklog": if model == "worklog":
if "contact" in payload and "contact_id" not in payload: if "contact" in payload and "contact_id" not in payload:
payload["contact_id"] = payload.pop("contact") payload["contact_id"] = payload.pop("contact")
@ -279,7 +287,28 @@ def init_entry_routes(app):
params = {} params = {}
if model == "inventory": if model == "inventory":
pass if "device_type_id" in payload:
payload["type_id"] = payload.pop("device_type_id")
for k in ("brand_id", "type_id", "owner_id", "location_id", "image_id"):
if payload.get(k) == "":
payload[k] = None
params = {
"fields": [
"barcode",
"name",
"serial",
"condition",
"model",
"notes",
"shared",
"timestamp",
"brand_id",
"type_id",
"image_id",
"location_id",
"owner_id",
]
}
elif model == "user": elif model == "user":
params = { params = {
"fields": [ "fields": [

View file

@ -1,7 +1,7 @@
<label class="form-label">Notes</label> <label class="form-label">Notes</label>
<div class="d-flex justify-content-between align-items-start"> <div class="d-flex justify-content-between align-items-start">
<div class="me-3 w-100 markdown-body" id="editContainer"></div> <div class="me-3 w-100 markdown-body" id="editContainer"></div>
<script type="application/json" id="noteContent">{{ field['template_ctx']['values']['notes'] | tojson }}</script> <script type="application/json" id="noteContent">{{ (field['template_ctx']['values']['notes'] if field['template_ctx']['values']['notes'] else '') | tojson }}</script>
<div class="form-check form-switch"> <div class="form-check form-switch">
<input type="checkbox" class="form-check-input" id="editSwitch" onchange="changeMode()" role="switch" <input type="checkbox" class="form-check-input" id="editSwitch" onchange="changeMode()" role="switch"
aria-label="Edit mode switch for inventory notes."> aria-label="Edit mode switch for inventory notes.">
@ -20,7 +20,7 @@
} }
@supports (field-sizing: content) { @supports (field-sizing: content) {
text-area.auto-md { textarea.auto-md {
field-sizing: content; field-sizing: content;
} }
} }
@ -35,7 +35,7 @@
function getMarkdown() { function getMarkdown() {
const el = document.getElementById('noteContent'); const el = document.getElementById('noteContent');
return el ? JSON.parse(el.textContent) : ""; return el ? (JSON.parse(el.textContent) || "") : "";
} }
function setMarkdown(md) { function setMarkdown(md) {

View file

@ -63,7 +63,8 @@
const json = formToJson(formEl); const json = formToJson(formEl);
if (model === 'inventory' && typeof getMarkdown === 'function') { if (model === 'inventory' && typeof getMarkdown === 'function') {
json.notes = getMarkdown().trim(); const md = getMarkdown();
json.notes = (typeof md === 'string') ? getMarkdown().trim() : '';
} else if (model === 'worklog') { } else if (model === 'worklog') {
json.updates = collectEditedUpdates(); json.updates = collectEditedUpdates();
json.delete_update_ids = collectDeletedIds(); json.delete_update_ids = collectDeletedIds();
@ -85,7 +86,7 @@
window.newDrafts = []; window.newDrafts = [];
window.deletedIds = []; window.deletedIds = [];
if (!hasId && reply.id) { if (!hasId && reply.id) {
window.location.href = `/entry/${model}/${reply.id}?pretty=1`; window.location.href = `/entry/${model}/${reply.id}`;
} }
} else { } else {
toastMessage(`Unable to save entry: ${reply.error}`, 'danger'); toastMessage(`Unable to save entry: ${reply.error}`, 'danger');

View file

@ -36,7 +36,7 @@
} }
@supports (field-sizing: content) { @supports (field-sizing: content) {
text-area.auto-md { textarea.auto-md {
field-sizing: content; field-sizing: content;
} }
} }