Update negative adjustment form

main
Wes Holland 10 months ago
parent 453d36dd86
commit 9e6b17b154

@ -6,18 +6,33 @@ use crate::session::SessionUser;
use askama::Template;
use askama_axum::{IntoResponse, Response};
use axum::extract::{Path, State};
use axum::{debug_handler, Form};
use axum::{async_trait, debug_handler, Form};
use axum_htmx::{HxEvent, HxResponseTrigger};
use serde::Deserialize;
use sqlx::SqlitePool;
use tracing::info;
use crate::app::routes::AppState;
use crate::util::form::extractors::{FormWithPathVars, ValidForm};
use crate::util::form::field_error::FieldError;
use crate::util::form::field_value::{FloatField, StringField};
use crate::util::form::validate_form::{ValidateForm, ValidateFormError};
#[derive(Template)]
#[derive(Template, Debug)]
#[template(path = "item/adjustment/negative-adjustment-form.html")]
pub struct NegativeAdjustmentFormTemplate {
pub item_id: i64,
pub amount_error: &'static str,
pub reason_error: &'static str,
pub amount: StringField,
pub reason: StringField,
}
impl Default for NegativeAdjustmentFormTemplate {
fn default() -> Self {
Self {
item_id: Default::default(),
amount: Default::default(),
reason: StringField::new(DbAdjustmentReason::Sale.into()),
}
}
}
#[derive(Deserialize, Debug)]
@ -26,50 +41,49 @@ pub struct NegativeAdjustmentFormData {
pub reason: Option<String>,
}
#[debug_handler]
#[async_trait]
impl ValidateForm for FormWithPathVars<i64, NegativeAdjustmentFormData> {
type ValidationErrorResponse = NegativeAdjustmentFormTemplate;
async fn validate(self, state: &AppState) -> Result<Self, ValidateFormError<Self::ValidationErrorResponse>> {
let item_id = self.path_data;
let fractional_units_allowed = does_inventory_item_allow_fractional_units(&state.db, item_id).await?;
let amount = FloatField::new(self.form_data.amount)
.invalid_if(|v| *v == 0.0 || v.is_nan() || v.is_sign_negative(), FieldError::PositiveNumber)
.invalid_if(|v| !(fractional_units_allowed || v.fract() == 0.0), FieldError::WholeNumber)
.map(|v| if fractional_units_allowed { format!("{:.2}", v) } else { format!("{:.0}", v) });
let reason = <Option<String> as Into<StringField>>::into(self.form_data.reason.clone())
.invalid_if(|v| v.is_empty(), FieldError::Required)
.invalid_if(|v| DbAdjustmentReason::try_from(v.as_str()).is_err(), FieldError::SelectOption);
if amount.is_error() || reason.is_error() {
return Err(ValidateFormError::ValidationError(Self::ValidationErrorResponse {
item_id,
amount,
reason,
}));
}
Ok(self)
}
}
pub async fn negative_adjustment_form_post(
State(db): State<SqlitePool>,
Path(id): Path<i64>,
user: SessionUser,
mut form_data: Form<NegativeAdjustmentFormData>,
mut data: ValidForm<FormWithPathVars<i64,NegativeAdjustmentFormData>>
) -> Result<Response, AppError> {
let adjustment_amount = if form_data.amount > 0.0 {
-1.0 * form_data.amount
} else {
form_data.amount
};
let fractional_units_allowed = does_inventory_item_allow_fractional_units(&db, id).await?;
let adjustment_amount = -1.0 * data.form_data.amount;
let reason = form_data
let reason = data.form_data
.reason
.take()
.and_then(|s| DbAdjustmentReason::try_from(s.as_str()).ok())
.unwrap_or_else(|| DbAdjustmentReason::Unknown);
let amount_error = if adjustment_amount == 0.0 {
"Please input a non-zero amount"
} else if !(fractional_units_allowed || adjustment_amount.fract() == 0.0) {
"Please input a whole number"
} else {
""
};
let reason_error = if reason == DbAdjustmentReason::Unknown {
"Unknown adjustment reason"
} else {
""
};
if !(amount_error.is_empty() && reason_error.is_empty()) {
return Ok(NegativeAdjustmentFormTemplate {
item_id: id,
amount_error,
reason_error,
}
.into_response());
}
let trigger_events = vec![
HxEvent::from("new-adjustment"),
HxEvent::from("form-submit-success"),
@ -79,7 +93,7 @@ pub async fn negative_adjustment_form_post(
info!(
"Add adjustment form: (Amount {}, Reason {:?}, for user {}",
form_data.amount, reason, user.name
data.form_data.amount, reason, user.name
);
let _new_id = add_adjustment(
@ -98,19 +112,18 @@ pub async fn negative_adjustment_form_post(
HxResponseTrigger::normal(trigger_events),
NegativeAdjustmentFormTemplate {
item_id: id,
amount_error: "",
reason_error: "",
..Default::default()
}
.into_response(),
)
.into_response())
}
#[debug_handler]
pub async fn negative_adjustment_form_get(Path(id): Path<i64>) -> Result<Response, AppError> {
Ok(NegativeAdjustmentFormTemplate {
item_id: id,
amount_error: "",
reason_error: "",
..Default::default()
}
.into_response())
}

@ -60,12 +60,12 @@ impl TryFrom<&str> for DbAdjustmentReason {
impl Into<String> for DbAdjustmentReason {
fn into(self) -> String {
match self {
Self::Unknown => { String::from("unknown") }
Self::Sale => { String::from("sale") }
Self::Destruction => { String::from("destruction") }
Self::Expiration => { String::from("expiration") }
Self::Theft => { String::from("theft") }
Self::NewStock => { String::from("new-stock") }
Self::Unknown => { String::from("Unknown") }
Self::Sale => { String::from("Sale") }
Self::Destruction => { String::from("Destruction") }
Self::Expiration => { String::from("Expiration") }
Self::Theft => { String::from("Theft") }
Self::NewStock => { String::from("New-stock") }
}
}
}

@ -1,4 +1,4 @@
use std::fmt::Debug;
use std::fmt::{Debug, Display};
use crate::util::form::field_error::FieldError;
/**
@ -20,7 +20,7 @@ pub type BoolField = FieldValue<bool, FieldError>;
pub type FloatField = FieldValue<f64, FieldError>;
impl<Value, Error> FieldValue<Value, Error>
where Value: Default
where Value: Default, Error: ToString
{
pub fn new(value: Value) -> Self {
Self { value, error: None }
@ -66,10 +66,22 @@ where Value: Default
error: self.error,
}
}
pub fn with_error(mut self, error: Error) -> Self {
self.set_error(error);
self
}
pub fn error_string(&self) -> String {
match self.error {
Some(ref error) => error.to_string(),
None => "Unknown".to_string(),
}
}
}
impl<Value, Error> Default for FieldValue<Value, Error>
where Value: Default,
where Value: Default, Error: ToString
{
fn default() -> Self {
Self::new(Value::default())
@ -85,4 +97,21 @@ where Value: Debug,
.field("error", &self.error)
.finish()
}
}
}
impl<Value, Error> From<Option<Value>> for FieldValue<Value, Error>
where Value: Default, Error: ToString {
fn from(value: Option<Value>) -> Self {
Self::new(value.unwrap_or_default())
}
}
impl<Value, Error> From<Result<Value, Error>> for FieldValue<Value, Error>
where Value: Default, Error: ToString {
fn from(value: Result<Value, Error>) -> Self {
match value {
Ok(value) => Self::new(value),
Err(error) => Self::default().with_error(error),
}
}
}

@ -3,7 +3,7 @@
hx-target="this"
hx-swap="outerHTML"
x-ref="formNegativeAdjustment"
x-data="{ reason: 'Sale', reason_dropdown_show: false }"
x-data="{ reason: '{{ reason.value }}', reason_dropdown_show: false }"
>
<div class="mb-5">
<label for="amount" class="mb-2 block text-sm font-medium">Amount</label>
@ -15,15 +15,16 @@
class="block max-w-56 rounded-lg border border-neutral-900 p-2.5 text-sm text-neutral-900 focus:border-paynes-gray focus:ring-paynes-gray dark:text-slate-100"
placeholder="Amount"
aria-label="amount"
value="{{ amount.value }}"
required
{% if !amount_error.is_empty() -%}
{% if !amount.is_error() -%}
aria-invalid="true"
aria-describedby="invalid-amount"
{% endif -%}
/>
{% if !amount_error.is_empty() -%}
{% if amount.is_error() -%}
<small id="invalid-amount" class="mt-2 text-sm text-cerise">
{{ amount_error }}
{{ amount.error_string() }}
</small>
{% endif -%}
</div>
@ -67,7 +68,6 @@
/>
</svg>
</button>
<!-- Dropdown Menu -->
<div
x-cloak
x-show="isOpen || openedWithKeyboard"

@ -24,7 +24,7 @@
<small id="invalid-amount"
class="mt-2 text-sm text-cerise"
>
{{ amount.error.unwrap() }}
{{ amount.error_string() }}
</small>
{% endif -%}
</div>
@ -48,7 +48,7 @@
<small id="invalid-price"
class="mt-2 text-sm text-cerise"
>
{{ price.error.unwrap() }}
{{ price.error_string() }}
</small>
{% endif -%}
</div>

@ -24,7 +24,7 @@
/>
{% if name.is_error() -%}
<small id="invalid-name" class="block mt-2 text-sm text-cerise">
{{ name.error.unwrap() }}
{{ name.error_string() }}
</small>
{% endif -%}
</div>
@ -46,7 +46,7 @@
/>
{% if reorder_point.is_error() -%}
<small id="invalid-reorder-point" class="block mt-2 text-sm text-cerise">
{{ reorder_point.error.unwrap() }}
{{ reorder_point.error_string() }}
</small>
{% endif -%}
</div>
@ -70,7 +70,7 @@
</select>
{% if display_unit_value.is_error() -%}
<small id="invalid-display-unit" class="block mt-2 text-sm text-cerise">
{{ display_unit_value.error.unwrap() }}
{{ display_unit_value.error_string() }}
</small>
{% endif -%}
</div>
@ -103,7 +103,7 @@
/>
{% if pims_id.is_error() -%}
<small id="invalid-pims-id" class="block mt-2 text-sm text-cerise">
{{ pims_id.error.unwrap() }}
{{ pims_id.error_string() }}
</small>
{% endif -%}
</div>
@ -124,7 +124,7 @@
/>
{% if vetcove_id.is_error() -%}
<small id="invalid-vetcove-id" class="block mt-2 text-sm text-cerise">
{{ vetcove_id.error.unwrap() }}
{{ vetcove_id.error_string() }}
</small>
{% endif -%}
</div>

Loading…
Cancel
Save

Powered by TurnKey Linux.