parent
801d37202e
commit
846d8d5dde
|
@ -0,0 +1,274 @@
|
||||
use serde::Serialize;
|
||||
use anyhow::Result;
|
||||
use chrono::{DateTime, NaiveDate, Utc};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
#[derive(sqlx::FromRow)]
|
||||
pub struct DbVetcoveItem {
|
||||
pub vetcove_item_id: i64,
|
||||
pub item_name: String,
|
||||
pub ignored: i64,
|
||||
pub manufacturer_name: Option<String>,
|
||||
pub manufacturer_number: Option<String>,
|
||||
pub category: Option<String>,
|
||||
pub units: Option<String>,
|
||||
pub covetrus_sku: Option<String>,
|
||||
pub mwi_sku: Option<String>,
|
||||
pub patterson_sku: Option<String>,
|
||||
pub midwest_sku: Option<String>,
|
||||
pub first_vet_sku: Option<String>,
|
||||
pub penn_vet_sku: Option<String>,
|
||||
pub amatheon_sku: Option<String>,
|
||||
pub victor_sku: Option<String>,
|
||||
pub vetcove_sku: Option<String>,
|
||||
pub miller_vet_sku: Option<String>,
|
||||
pub boehringer_ingelheim_sku: Option<String>,
|
||||
pub zoetis_sku: Option<String>,
|
||||
pub pharmsource_ah_sku: Option<String>,
|
||||
pub ne_animal_health_sku: Option<String>,
|
||||
pub dechra_sku: Option<String>,
|
||||
pub medline_sku: Option<String>,
|
||||
pub elanco_sku: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
pub async fn add_vetcove_item(db: &SqlitePool,
|
||||
vetcove_item_id: i64,
|
||||
item_name: String,
|
||||
ignored: bool,
|
||||
manufacturer_name: Option<String>,
|
||||
manufacturer_number: Option<String>,
|
||||
category: Option<String>,
|
||||
units: Option<String>,
|
||||
covetrus_sku: Option<String>,
|
||||
mwi_sku: Option<String>,
|
||||
patterson_sku: Option<String>,
|
||||
midwest_sku: Option<String>,
|
||||
first_vet_sku: Option<String>,
|
||||
penn_vet_sku: Option<String>,
|
||||
amatheon_sku: Option<String>,
|
||||
victor_sku: Option<String>,
|
||||
vetcove_sku: Option<String>,
|
||||
miller_vet_sku: Option<String>,
|
||||
boehringer_ingelheim_sku: Option<String>,
|
||||
zoetis_sku: Option<String>,
|
||||
pharmsource_ah_sku: Option<String>,
|
||||
ne_animal_health_sku: Option<String>,
|
||||
dechra_sku: Option<String>,
|
||||
medline_sku: Option<String>,
|
||||
elanco_sku: Option<String>)
|
||||
-> Result<Option<i64>> {
|
||||
|
||||
let existing: i64 = sqlx::query_scalar(r#"
|
||||
SELECT COUNT(1) FROM VetcoveItem WHERE vetcove_item_id = ?
|
||||
"#).bind(vetcove_item_id).fetch_one(db).await?;
|
||||
|
||||
if existing > 0 {
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
let res = sqlx::query(
|
||||
r#"
|
||||
INSERT INTO VetcoveItem (
|
||||
vetcove_item_id,
|
||||
item_name,
|
||||
ignored,
|
||||
manufacturer_name,
|
||||
manufacturer_number,
|
||||
category,
|
||||
units,
|
||||
covetrus_sku,
|
||||
mwi_sku,
|
||||
patterson_sku,
|
||||
midwest_sku,
|
||||
first_vet_sku,
|
||||
penn_vet_sku,
|
||||
amatheon_sku,
|
||||
victor_sku,
|
||||
vetcove_sku,
|
||||
miller_vet_sku,
|
||||
boehringer_ingelheim_sku,
|
||||
zoetis_sku,
|
||||
pharmsource_ah_sku,
|
||||
ne_animal_health_sku,
|
||||
dechra_sku,
|
||||
medline_sku,
|
||||
elanco_sku)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
"#)
|
||||
.bind(vetcove_item_id)
|
||||
.bind(item_name)
|
||||
.bind(ignored)
|
||||
.bind(manufacturer_name)
|
||||
.bind(manufacturer_number)
|
||||
.bind(category)
|
||||
.bind(units)
|
||||
.bind(covetrus_sku)
|
||||
.bind(mwi_sku)
|
||||
.bind(patterson_sku)
|
||||
.bind(midwest_sku)
|
||||
.bind(first_vet_sku)
|
||||
.bind(penn_vet_sku)
|
||||
.bind(amatheon_sku)
|
||||
.bind(victor_sku)
|
||||
.bind(vetcove_sku)
|
||||
.bind(miller_vet_sku)
|
||||
.bind(boehringer_ingelheim_sku)
|
||||
.bind(zoetis_sku)
|
||||
.bind(pharmsource_ah_sku)
|
||||
.bind(ne_animal_health_sku)
|
||||
.bind(dechra_sku)
|
||||
.bind(medline_sku)
|
||||
.bind(elanco_sku)
|
||||
.execute(db).await?;
|
||||
|
||||
let _new_id = res.last_insert_rowid();
|
||||
|
||||
Ok(Some(vetcove_item_id))
|
||||
}
|
||||
|
||||
pub async fn get_vetcove_item_by_id(db: &SqlitePool, vetcove_item_id: i64) -> Result<Option<DbVetcoveItem>> {
|
||||
sqlx::query_as!(
|
||||
DbVetcoveItem,
|
||||
r#"
|
||||
SELECT
|
||||
vetcove_item_id,
|
||||
item_name,
|
||||
ignored,
|
||||
manufacturer_name,
|
||||
manufacturer_number,
|
||||
category,
|
||||
units,
|
||||
covetrus_sku,
|
||||
mwi_sku,
|
||||
patterson_sku,
|
||||
midwest_sku,
|
||||
first_vet_sku,
|
||||
penn_vet_sku,
|
||||
amatheon_sku,
|
||||
victor_sku,
|
||||
vetcove_sku,
|
||||
miller_vet_sku,
|
||||
boehringer_ingelheim_sku,
|
||||
zoetis_sku,
|
||||
pharmsource_ah_sku,
|
||||
ne_animal_health_sku,
|
||||
dechra_sku,
|
||||
medline_sku,
|
||||
elanco_sku
|
||||
FROM
|
||||
VetcoveItem
|
||||
WHERE VetcoveItem.vetcove_item_id = ?
|
||||
"#,
|
||||
vetcove_item_id
|
||||
)
|
||||
.fetch_optional(db).await
|
||||
.map_err(From::from)
|
||||
}
|
||||
|
||||
pub async fn get_vetcove_item_ignored(db: &SqlitePool, vetcove_item_id: i64)
|
||||
-> Result<bool> {
|
||||
|
||||
let query_res: i64 = sqlx::query_scalar(r#"
|
||||
SELECT ignored from VetcoveItem WHERE vetcove_item_id = ?
|
||||
"#)
|
||||
.bind(vetcove_item_id)
|
||||
.fetch_one(db).await?;
|
||||
|
||||
let ignored = query_res != 0;
|
||||
|
||||
Ok(ignored)
|
||||
}
|
||||
|
||||
pub async fn set_vetcove_item_ignored(db: &SqlitePool, vetcove_item_id: i64, ignored: bool)
|
||||
-> Result<()> {
|
||||
|
||||
let affected = sqlx::query(r#"
|
||||
UPDATE VetcoveItem SET ignored = ? WHERE vetcove_item_id = ?
|
||||
"#)
|
||||
.bind(ignored)
|
||||
.bind(vetcove_item_id)
|
||||
.execute(db).await?.rows_affected();
|
||||
|
||||
assert_eq!(affected, 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn add_vetcove_item_to_inventory_item_mapping(db: &SqlitePool,
|
||||
vetcove_item_id: i64,
|
||||
inventory_item_id: i64)
|
||||
-> Result<i64> {
|
||||
let res = sqlx::query(
|
||||
r#"
|
||||
INSERT INTO VetcoveItemToInventoryItemMap (
|
||||
vetcove_item_id,
|
||||
inventory_item_id
|
||||
)
|
||||
VALUES (?, ?)
|
||||
"#)
|
||||
.bind(vetcove_item_id)
|
||||
.bind(inventory_item_id)
|
||||
.execute(db).await?;
|
||||
|
||||
let new_id = res.last_insert_rowid();
|
||||
|
||||
Ok(new_id)
|
||||
}
|
||||
|
||||
pub async fn map_vetcove_item_to_inventory_item(db: &SqlitePool,
|
||||
vetcove_item_id: i64,
|
||||
inventory_item_id: i64)
|
||||
-> Result<()> {
|
||||
|
||||
let existing: i64 = sqlx::query_scalar(r#"
|
||||
SELECT COUNT(1) FROM VetcoveItemToInventoryItemMap WHERE vetcove_item_id = ?
|
||||
"#).bind(vetcove_item_id).fetch_one(db).await?;
|
||||
|
||||
if existing > 0 {
|
||||
let res = sqlx::query(r#"
|
||||
UPDATE VetcoveItemToInventoryItemMap
|
||||
SET inventory_item_id = ?
|
||||
WHERE vetcove_item_id = ?
|
||||
"#)
|
||||
.bind(inventory_item_id)
|
||||
.bind(vetcove_item_id)
|
||||
.execute(db).await?.rows_affected();
|
||||
|
||||
assert_eq!(res, 1);
|
||||
}
|
||||
else {
|
||||
let res = sqlx::query(r#"
|
||||
INSERT INTO VetcoveItemToInventoryItemMap (vetcove_item_id, inventory_item_id)
|
||||
VALUES (?, ?)
|
||||
"#)
|
||||
.bind(vetcove_item_id)
|
||||
.bind(inventory_item_id)
|
||||
.execute(db).await?.rows_affected();
|
||||
|
||||
assert_eq!(res, 1);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn get_inventory_item_id_by_vetcove_item_id(db: &SqlitePool, vetcove_item_id: i64)
|
||||
-> Result<Option<i64>> {
|
||||
|
||||
let value: Option<i64> = sqlx::query_scalar(r#"
|
||||
SELECT inventory_item_id FROM VetcoveItemToInventoryItemMap WHERE vetcove_item_id = ?
|
||||
"#).bind(vetcove_item_id).fetch_optional(db).await?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
pub async fn get_vetcove_item_ids_by_inventory_item_id(db: &SqlitePool, inventory_item_id: i64)
|
||||
-> Result<Vec<i64>> {
|
||||
|
||||
let value: Vec<i64> = sqlx::query_scalar(r#"
|
||||
SELECT inventory_item_id FROM VetcoveItemToInventoryItemMap WHERE vetcove_item_id = ?
|
||||
"#).bind(inventory_item_id).fetch_all(db).await?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
@ -1 +1,2 @@
|
||||
pub mod item_history;
|
||||
pub mod item;
|
||||
@ -1,52 +0,0 @@
|
||||
{% extends "main.html" %} {% block title %} Upload {% endblock %} {% block
|
||||
content %}
|
||||
|
||||
<div class="relative h-auto mx-auto max-w-[68.75rem] w-[95vw] px-4 py-4">
|
||||
<form
|
||||
action="/upload/catalog"
|
||||
method="post"
|
||||
enctype="multipart/form-data"
|
||||
x-data="{ file: '' }"
|
||||
>
|
||||
<fieldset class="grid">
|
||||
<h3>Catalog Import</h3>
|
||||
<label role="button" class="secondary" x-show="!file">
|
||||
Choose File
|
||||
<input
|
||||
type="file"
|
||||
name="file"
|
||||
x-model="file"
|
||||
style="display: none"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
<input type="submit" value="Upload" x-show="file" />
|
||||
<input type="reset" value="Cancel" x-show="file" />
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<form
|
||||
action="/upload/vetcove/history"
|
||||
method="post"
|
||||
enctype="multipart/form-data"
|
||||
x-data="{ file: '' }"
|
||||
>
|
||||
<fieldset class="grid">
|
||||
<h3>Vetcove Item History</h3>
|
||||
<label role="button" class="secondary" x-show="!file">
|
||||
Choose File
|
||||
<input
|
||||
type="file"
|
||||
name="file"
|
||||
x-model="file"
|
||||
style="display: none"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
<input type="submit" value="Upload" x-show="file" />
|
||||
<input type="reset" value="Cancel" x-show="file" />
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@ -0,0 +1,91 @@
|
||||
{% extends "main.html" %} {% block title %} Upload {% endblock %} {% block
|
||||
content %}
|
||||
|
||||
<div class="relative h-auto mx-auto max-w-[68.75rem] w-[95vw] px-4 py-4">
|
||||
<form
|
||||
action="/upload/catalog"
|
||||
method="post"
|
||||
enctype="multipart/form-data"
|
||||
x-data="{ file: '' }"
|
||||
>
|
||||
<fieldset class="grid">
|
||||
<h3>Catalog Import</h3>
|
||||
<label role="button" class="secondary" x-show="!file">
|
||||
Choose File
|
||||
<input
|
||||
type="file"
|
||||
name="file"
|
||||
x-model="file"
|
||||
style="display: none"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
<input type="submit" value="Upload" x-show="file" />
|
||||
<input type="reset" value="Cancel" x-show="file" />
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
<form
|
||||
id="vetcove-history-upload"
|
||||
enctype="multipart/form-data"
|
||||
hx-post="/upload/vetcove/history"
|
||||
hx-target="#upload-results"
|
||||
x-data="{ file: '' }"
|
||||
x-on:htmx:response-error="$dispatch('notice', {type: 'error', text: 'Unknown error'})"
|
||||
>
|
||||
<h3>Vetcove Item History</h3>
|
||||
|
||||
<label role="button"
|
||||
class="mb-2 me-2 rounded-lg bg-english-violet px-5 py-2.5 text-sm font-medium text-slate-100 hover:bg-dark-cyan focus:outline-none focus:ring-4 focus:ring-dark-cyan"
|
||||
x-show="!file">
|
||||
Choose File
|
||||
<input
|
||||
type="file"
|
||||
name="file"
|
||||
x-model="file"
|
||||
style="display: none"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
<input type="submit" value="Upload"
|
||||
class="mb-2 me-2 rounded-lg bg-english-violet px-5 py-2.5 text-sm font-medium text-slate-100 hover:bg-dark-cyan focus:outline-none focus:ring-4 focus:ring-dark-cyan"
|
||||
x-show="file" />
|
||||
<input type="reset" value="Cancel"
|
||||
class="mb-2 me-2 rounded-lg bg-english-violet px-5 py-2.5 text-sm font-medium text-slate-100 hover:bg-dark-cyan focus:outline-none focus:ring-4 focus:ring-dark-cyan"
|
||||
x-show="file" />
|
||||
</form>
|
||||
|
||||
<form
|
||||
id="vetcove-items-upload"
|
||||
enctype="multipart/form-data"
|
||||
hx-post="/upload/vetcove/items"
|
||||
hx-target="#upload-results"
|
||||
x-data="{ file: '' }"
|
||||
x-on:htmx:response-error="$dispatch('notice', {type: 'error', text: 'Unknown error'})"
|
||||
>
|
||||
<h3>Vetcove Items</h3>
|
||||
|
||||
<label role="button"
|
||||
class="mb-2 me-2 rounded-lg bg-english-violet px-5 py-2.5 text-sm font-medium text-slate-100 hover:bg-dark-cyan focus:outline-none focus:ring-4 focus:ring-dark-cyan"
|
||||
x-show="!file">
|
||||
Choose File
|
||||
<input
|
||||
type="file"
|
||||
name="file"
|
||||
x-model="file"
|
||||
style="display: none"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
<input type="submit" value="Upload"
|
||||
class="mb-2 me-2 rounded-lg bg-english-violet px-5 py-2.5 text-sm font-medium text-slate-100 hover:bg-dark-cyan focus:outline-none focus:ring-4 focus:ring-dark-cyan"
|
||||
x-show="file" />
|
||||
<input type="reset" value="Cancel"
|
||||
class="mb-2 me-2 rounded-lg bg-english-violet px-5 py-2.5 text-sm font-medium text-slate-100 hover:bg-dark-cyan focus:outline-none focus:ring-4 focus:ring-dark-cyan"
|
||||
x-show="file" />
|
||||
</form>
|
||||
|
||||
<div id="upload-results"></div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@ -0,0 +1,39 @@
|
||||
<table class="w-full text-left text-sm rtl:text-right">
|
||||
<thead class="bg-gray-50 text-xs uppercase text-gray-700 dark:bg-gray-700 dark:text-gray-400">
|
||||
<tr>
|
||||
<th class="px-6 py-3" scope="col">Status</th>
|
||||
<th class="px-6 py-3" scope="col">Vetcove ID</th>
|
||||
<th class="px-6 py-3" scope="col">Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for res in results %}
|
||||
{% match res.error %}
|
||||
{% when Some(ItemHistoryImportRowErrorKind::AlreadyImported) %}
|
||||
<tr class="text-slate-500">
|
||||
<td class="px-6 py-4"> Skip </td>
|
||||
<td class="px-6 py-4">{{ res.item.vetcove_item_id }}</td>
|
||||
<td class="px-6 py-4">{{ res.item.item_name }}</td>
|
||||
</tr>
|
||||
{% when Some(ItemHistoryImportRowErrorKind::VetcoveIdNotFound) %}
|
||||
<tr class="text-cerise">
|
||||
<td class="px-6 py-4"> Id Not Found </td>
|
||||
<td class="px-6 py-4">{{ res.item.vetcove_item_id }}</td>
|
||||
<td class="px-6 py-4">{{ res.item.item_name }}</td>
|
||||
</tr>
|
||||
{% when None %}
|
||||
<tr class="text-green-700">
|
||||
<td class="px-6 py-4"> Success </td>
|
||||
<td class="px-6 py-4">{{ res.item.vetcove_item_id }}</td>
|
||||
<td class="px-6 py-4">{{ res.item.item_name }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr class="text-cerise">
|
||||
<td class="px-6 py-4"> Unknown Error </td>
|
||||
<td class="px-6 py-4">{{ res.item.vetcove_item_id }}</td>
|
||||
<td class="px-6 py-4">{{ res.item.item_name }}</td>
|
||||
</tr>
|
||||
{% endmatch %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
@ -0,0 +1,26 @@
|
||||
<table class="w-full text-left text-sm rtl:text-right">
|
||||
<thead class="bg-gray-50 text-xs uppercase text-gray-700 dark:bg-gray-700 dark:text-gray-400">
|
||||
<tr>
|
||||
<th class="px-6 py-3" scope="col">Status</th>
|
||||
<th class="px-6 py-3" scope="col">Vetcove ID</th>
|
||||
<th class="px-6 py-3" scope="col">Name</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for res in results %}
|
||||
{% if res.already_imported %}
|
||||
<tr class="text-slate-500">
|
||||
<td class="px-6 py-4"> Skip </td>
|
||||
<td class="px-6 py-4">{{ res.item.vetcove_item_id }}</td>
|
||||
<td class="px-6 py-4">{{ res.item.item_name }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr class="text-green-700">
|
||||
<td class="px-6 py-4"> Imported </td>
|
||||
<td class="px-6 py-4">{{ res.item.vetcove_item_id }}</td>
|
||||
<td class="px-6 py-4">{{ res.item.item_name }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
Loading…
Reference in new issue