You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
143 lines
5.0 KiB
143 lines
5.0 KiB
use crate::error::AppError;
|
|
use crate::session::SessionUser;
|
|
use askama::Template;
|
|
use askama_axum::{IntoResponse, Response};
|
|
use axum::extract::State;
|
|
use axum::{async_trait, debug_handler};
|
|
use axum_htmx::{HxEvent, HxResponseTrigger};
|
|
use serde::Deserialize;
|
|
use crate::app::routes::AppState;
|
|
use crate::db::display_unit::DbDisplayUnit;
|
|
use crate::db;
|
|
use crate::util::form::field_error::FieldError;
|
|
use crate::util::form::field_value::{BoolField, FloatField, StringField};
|
|
use crate::util::form::validate_form::{ValidateForm, ValidateFormError};
|
|
use crate::util::form::helpers::deserialize_form_checkbox;
|
|
use crate::util::form::base_response::BaseResponse;
|
|
use crate::util::form::extractors::{FormBase, ValidForm};
|
|
|
|
#[derive(Template, Debug, Default)]
|
|
#[template(path = "item/item-create-form.html")]
|
|
pub struct CreateItemFormTemplate {
|
|
pub display_units: Vec<DbDisplayUnit>,
|
|
pub name: StringField,
|
|
pub display_unit_value: StringField,
|
|
pub reorder_point: StringField,
|
|
pub pims_id: StringField,
|
|
pub vetcove_id: StringField,
|
|
pub allow_fractional_units: BoolField,
|
|
}
|
|
|
|
#[async_trait]
|
|
impl BaseResponse for CreateItemFormTemplate {
|
|
type ResponseType = Self;
|
|
|
|
async fn base_response(state: &AppState) -> Result<Self::ResponseType, AppError> {
|
|
let db = &state.db;
|
|
let display_units = db::display_unit::get_display_units(&db).await?;
|
|
|
|
Ok(Self {
|
|
display_units,
|
|
..Default::default()
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
pub struct CreateItemFormData {
|
|
name: String,
|
|
display_unit: String,
|
|
reorder_point: f64,
|
|
pims_id: Option<String>,
|
|
vetcove_id: Option<String>,
|
|
#[serde(default, deserialize_with = "deserialize_form_checkbox")]
|
|
allow_fractional_units: bool,
|
|
}
|
|
|
|
#[async_trait]
|
|
impl ValidateForm for FormBase<CreateItemFormData> {
|
|
type ValidationErrorResponse = CreateItemFormTemplate;
|
|
|
|
async fn validate(self, state: &AppState) -> Result<Self, ValidateFormError<Self::ValidationErrorResponse>> {
|
|
let display_units = db::display_unit::get_display_units(&state.db).await?;
|
|
|
|
let name = StringField::new(self.form_data.name.clone())
|
|
.invalid_if(|v| v.is_empty(), FieldError::Required);
|
|
|
|
let display_unit_value = StringField::new(self.form_data.display_unit.clone())
|
|
.invalid_if(|v| v.is_empty(), FieldError::Required)
|
|
.invalid_if(|v| !display_units.iter().any(
|
|
|x| x.abbreviation.eq(&self.form_data.display_unit)),
|
|
FieldError::SelectOption)
|
|
.clear_value_if_error();
|
|
|
|
let reorder_point = FloatField::new(self.form_data.reorder_point.clone())
|
|
.invalid_if(|v| v.is_nan() || v.is_infinite() || v.is_sign_negative(), FieldError::PositiveNumber)
|
|
.invalid_if(|v| !(self.form_data.allow_fractional_units || v.fract() == 0.0), FieldError::WholeNumber)
|
|
.map(|v| format!("{:.2}", v));
|
|
|
|
let pims_id = StringField::new(self.form_data.pims_id.clone().unwrap_or_default())
|
|
.invalid_if(|v| v.chars().any(char::is_whitespace), FieldError::ValidIdentifier);
|
|
|
|
let vetcove_id = StringField::new(self.form_data.vetcove_id.clone().unwrap_or_default())
|
|
.invalid_if(|v| !v.chars().all(|c| c.is_ascii_digit()), FieldError::ValidIdentifier);
|
|
|
|
let allow_fractional_units = BoolField::new(self.form_data.allow_fractional_units);
|
|
|
|
if name.is_error() ||
|
|
display_unit_value.is_error() ||
|
|
reorder_point.is_error() ||
|
|
pims_id.is_error() ||
|
|
vetcove_id.is_error() {
|
|
|
|
return Err(ValidateFormError::ValidationError(
|
|
Self::ValidationErrorResponse {
|
|
display_units,
|
|
name,
|
|
display_unit_value,
|
|
reorder_point,
|
|
pims_id,
|
|
vetcove_id,
|
|
allow_fractional_units,
|
|
}));
|
|
}
|
|
|
|
Ok(self)
|
|
}
|
|
}
|
|
|
|
#[debug_handler]
|
|
pub async fn create_item_form_post(
|
|
State(state): State<AppState>,
|
|
user: SessionUser,
|
|
form: ValidForm<FormBase<CreateItemFormData>>,
|
|
) -> Result<Response, AppError> {
|
|
|
|
let form_data = &form.form_data;
|
|
|
|
let _new_id = db::inventory_item::add_inventory_item(&state.db, &form_data.name, form_data.reorder_point,
|
|
form_data.allow_fractional_units, &form_data.display_unit,
|
|
&form_data.pims_id, &form_data.vetcove_id,
|
|
).await?;
|
|
|
|
let fresh_form = CreateItemFormTemplate::base_response(&state).await?;
|
|
|
|
let events = vec![
|
|
HxEvent::from("form-submit-success"),
|
|
HxEvent::from("new-item"),
|
|
];
|
|
|
|
Ok( (
|
|
HxResponseTrigger::normal(events),
|
|
fresh_form.into_response()
|
|
).into_response() )
|
|
}
|
|
|
|
#[debug_handler]
|
|
pub async fn create_item_form_get(
|
|
State(state): State<AppState>,
|
|
) -> Result<Response, AppError> {
|
|
Ok(CreateItemFormTemplate::base_response(&state).await?.into_response())
|
|
}
|