fixed issues with the import
This commit is contained in:
@@ -185,6 +185,9 @@ message PostTableDataResponse {
|
|||||||
|
|
||||||
// Journal created or updated by automatic accounting, when applicable.
|
// Journal created or updated by automatic accounting, when applicable.
|
||||||
optional int64 journal_id = 5;
|
optional int64 journal_id = 5;
|
||||||
|
|
||||||
|
// Non-blocking data-quality warnings produced while inserting the row.
|
||||||
|
repeated string warnings = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message BeginTableDataImportRequest {
|
message BeginTableDataImportRequest {
|
||||||
@@ -218,9 +221,17 @@ message CommitTableDataImportRequest {
|
|||||||
string import_id = 1;
|
string import_id = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message TableDataImportWarning {
|
||||||
|
// Zero-based position in the staged import, matching failed-row metadata.
|
||||||
|
int64 row_index = 1;
|
||||||
|
string table_name = 2;
|
||||||
|
string message = 3;
|
||||||
|
}
|
||||||
|
|
||||||
message CommitTableDataImportResponse {
|
message CommitTableDataImportResponse {
|
||||||
bool success = 1;
|
bool success = 1;
|
||||||
int64 inserted_rows = 2;
|
int64 inserted_rows = 2;
|
||||||
|
repeated TableDataImportWarning warnings = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AbortTableDataImportRequest {
|
message AbortTableDataImportRequest {
|
||||||
|
|||||||
Binary file not shown.
@@ -77,6 +77,9 @@ pub struct PostTableDataResponse {
|
|||||||
/// Journal created or updated by automatic accounting, when applicable.
|
/// Journal created or updated by automatic accounting, when applicable.
|
||||||
#[prost(int64, optional, tag = "5")]
|
#[prost(int64, optional, tag = "5")]
|
||||||
pub journal_id: ::core::option::Option<i64>,
|
pub journal_id: ::core::option::Option<i64>,
|
||||||
|
/// Non-blocking data-quality warnings produced while inserting the row.
|
||||||
|
#[prost(string, repeated, tag = "6")]
|
||||||
|
pub warnings: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
|
||||||
}
|
}
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||||
pub struct BeginTableDataImportRequest {
|
pub struct BeginTableDataImportRequest {
|
||||||
@@ -122,12 +125,24 @@ pub struct CommitTableDataImportRequest {
|
|||||||
#[prost(string, tag = "1")]
|
#[prost(string, tag = "1")]
|
||||||
pub import_id: ::prost::alloc::string::String,
|
pub import_id: ::prost::alloc::string::String,
|
||||||
}
|
}
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
|
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||||
|
pub struct TableDataImportWarning {
|
||||||
|
/// Zero-based position in the staged import, matching failed-row metadata.
|
||||||
|
#[prost(int64, tag = "1")]
|
||||||
|
pub row_index: i64,
|
||||||
|
#[prost(string, tag = "2")]
|
||||||
|
pub table_name: ::prost::alloc::string::String,
|
||||||
|
#[prost(string, tag = "3")]
|
||||||
|
pub message: ::prost::alloc::string::String,
|
||||||
|
}
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct CommitTableDataImportResponse {
|
pub struct CommitTableDataImportResponse {
|
||||||
#[prost(bool, tag = "1")]
|
#[prost(bool, tag = "1")]
|
||||||
pub success: bool,
|
pub success: bool,
|
||||||
#[prost(int64, tag = "2")]
|
#[prost(int64, tag = "2")]
|
||||||
pub inserted_rows: i64,
|
pub inserted_rows: i64,
|
||||||
|
#[prost(message, repeated, tag = "3")]
|
||||||
|
pub warnings: ::prost::alloc::vec::Vec<TableDataImportWarning>,
|
||||||
}
|
}
|
||||||
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
|
||||||
pub struct AbortTableDataImportRequest {
|
pub struct AbortTableDataImportRequest {
|
||||||
|
|||||||
@@ -60,12 +60,17 @@ process_csv() {
|
|||||||
cleaned = clean_account($5)
|
cleaned = clean_account($5)
|
||||||
key = key_value(cleaned)
|
key = key_value(cleaned)
|
||||||
|
|
||||||
|
if (key == "") {
|
||||||
|
cleaned = "\"000\""
|
||||||
|
key = "000"
|
||||||
|
}
|
||||||
|
|
||||||
$5 = cleaned
|
$5 = cleaned
|
||||||
|
|
||||||
# Deleted:
|
# Deleted:
|
||||||
# "000" -> true
|
# "000" -> true
|
||||||
# "000/" -> cleaned to "000" -> true
|
# "000/" -> cleaned to "000" -> true
|
||||||
# "" -> true
|
# "" -> changed to "000" -> true
|
||||||
#
|
#
|
||||||
# Everything else -> false
|
# Everything else -> false
|
||||||
if (key == "000" || key == "") {
|
if (key == "000" || key == "") {
|
||||||
|
|||||||
@@ -616,6 +616,7 @@ import-success-message = Vloženo { $inserted ->
|
|||||||
[few] { $source_rows } připravených řádků
|
[few] { $source_rows } připravených řádků
|
||||||
*[other] { $source_rows } připravených řádků
|
*[other] { $source_rows } připravených řádků
|
||||||
}.
|
}.
|
||||||
|
import-success-warnings = Upozornění: { $warnings }
|
||||||
|
|
||||||
# --- Import v průběhu -------------------------------------------------------
|
# --- Import v průběhu -------------------------------------------------------
|
||||||
import-progress-heading = Importuje se do { $table }
|
import-progress-heading = Importuje se do { $table }
|
||||||
|
|||||||
@@ -605,6 +605,7 @@ import-success-message = Inserted { $inserted ->
|
|||||||
[one] { $source_rows } prepared row
|
[one] { $source_rows } prepared row
|
||||||
*[other] { $source_rows } prepared rows
|
*[other] { $source_rows } prepared rows
|
||||||
}.
|
}.
|
||||||
|
import-success-warnings = Warnings: { $warnings }
|
||||||
|
|
||||||
# --- The import while it runs ----------------------------------------------
|
# --- The import while it runs ----------------------------------------------
|
||||||
import-progress-heading = Importing into { $table }
|
import-progress-heading = Importing into { $table }
|
||||||
|
|||||||
@@ -614,6 +614,7 @@ import-success-message = Vložený { $inserted ->
|
|||||||
[one] { $source_rows } pripraveného riadka
|
[one] { $source_rows } pripraveného riadka
|
||||||
*[other] { $source_rows } pripravených riadkov
|
*[other] { $source_rows } pripravených riadkov
|
||||||
}.
|
}.
|
||||||
|
import-success-warnings = Upozornenia: { $warnings }
|
||||||
|
|
||||||
# --- Import počas behu ------------------------------------------------------
|
# --- Import počas behu ------------------------------------------------------
|
||||||
import-progress-heading = Importuje sa do { $table }
|
import-progress-heading = Importuje sa do { $table }
|
||||||
|
|||||||
@@ -302,11 +302,12 @@ pub(crate) async fn import_progress(
|
|||||||
|
|
||||||
let response = match &snapshot.outcome {
|
let response = match &snapshot.outcome {
|
||||||
None => Html(ui::render_progress(locale, &id, &snapshot)).into_response(),
|
None => Html(ui::render_progress(locale, &id, &snapshot)).into_response(),
|
||||||
Some(Outcome::Succeeded) => Html(ui::render_success(
|
Some(Outcome::Succeeded { warnings }) => Html(ui::render_success(
|
||||||
locale,
|
locale,
|
||||||
snapshot.inserted,
|
snapshot.inserted,
|
||||||
snapshot.total_rows,
|
snapshot.total_rows,
|
||||||
&snapshot.table_name,
|
&snapshot.table_name,
|
||||||
|
warnings,
|
||||||
))
|
))
|
||||||
.into_response(),
|
.into_response(),
|
||||||
Some(Outcome::RowFailed {
|
Some(Outcome::RowFailed {
|
||||||
@@ -445,8 +446,21 @@ async fn run_import(job: Running) {
|
|||||||
};
|
};
|
||||||
match committed {
|
match committed {
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
let inserted = usize::try_from(response.into_inner().inserted_rows).unwrap_or(staged);
|
let response = response.into_inner();
|
||||||
jobs.finish(&job.id, inserted, Outcome::Succeeded);
|
let inserted = usize::try_from(response.inserted_rows).unwrap_or(staged);
|
||||||
|
let warnings = response
|
||||||
|
.warnings
|
||||||
|
.into_iter()
|
||||||
|
.map(|warning| {
|
||||||
|
format!(
|
||||||
|
"CSV row {} ({}): {}",
|
||||||
|
warning.row_index + 2,
|
||||||
|
warning.table_name,
|
||||||
|
warning.message,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
jobs.finish(&job.id, inserted, Outcome::Succeeded { warnings });
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
abort_import(&job, &import_id).await;
|
abort_import(&job, &import_id).await;
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ const KEEP_FINISHED: Duration = Duration::from_secs(300);
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) enum Outcome {
|
pub(crate) enum Outcome {
|
||||||
/// Every staged row was committed atomically.
|
/// Every staged row was committed atomically.
|
||||||
Succeeded,
|
Succeeded { warnings: Vec<String> },
|
||||||
/// The backend stopped on one row. No rows from the import were committed.
|
/// The backend stopped on one row. No rows from the import were committed.
|
||||||
RowFailed {
|
RowFailed {
|
||||||
status: StatusCode,
|
status: StatusCode,
|
||||||
@@ -255,7 +255,7 @@ mod tests {
|
|||||||
assert!(jobs.is_running("session"));
|
assert!(jobs.is_running("session"));
|
||||||
assert!(!jobs.is_running("other"));
|
assert!(!jobs.is_running("other"));
|
||||||
|
|
||||||
jobs.finish(&id, 10, Outcome::Succeeded);
|
jobs.finish(&id, 10, Outcome::Succeeded { warnings: Vec::new() });
|
||||||
assert!(!jobs.is_running("session"));
|
assert!(!jobs.is_running("session"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,7 +267,7 @@ mod tests {
|
|||||||
inserted: 0,
|
inserted: 0,
|
||||||
total_rows: 0,
|
total_rows: 0,
|
||||||
elapsed: Duration::from_secs(1),
|
elapsed: Duration::from_secs(1),
|
||||||
outcome: Some(Outcome::Succeeded),
|
outcome: Some(Outcome::Succeeded { warnings: Vec::new() }),
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_eq!(snapshot.percent(), 100);
|
assert_eq!(snapshot.percent(), 100);
|
||||||
|
|||||||
@@ -169,17 +169,27 @@ pub(crate) fn render_success(
|
|||||||
inserted: usize,
|
inserted: usize,
|
||||||
prepared_rows: usize,
|
prepared_rows: usize,
|
||||||
table_name: &str,
|
table_name: &str,
|
||||||
|
warnings: &[String],
|
||||||
) -> String {
|
) -> String {
|
||||||
render(&ImportSuccessDialog {
|
let mut message = tr!(
|
||||||
locale,
|
|
||||||
title: &tr!(locale, "import-success-title"),
|
|
||||||
message: &tr!(
|
|
||||||
locale,
|
locale,
|
||||||
"import-success-message",
|
"import-success-message",
|
||||||
"inserted" => inserted as i64,
|
"inserted" => inserted as i64,
|
||||||
"source_rows" => prepared_rows as i64,
|
"source_rows" => prepared_rows as i64,
|
||||||
"table" => table_name.to_string(),
|
"table" => table_name.to_string(),
|
||||||
),
|
);
|
||||||
|
if !warnings.is_empty() {
|
||||||
|
message.push(' ');
|
||||||
|
message.push_str(&tr!(
|
||||||
|
locale,
|
||||||
|
"import-success-warnings",
|
||||||
|
"warnings" => warnings.join("; "),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
render(&ImportSuccessDialog {
|
||||||
|
locale,
|
||||||
|
title: &tr!(locale, "import-success-title"),
|
||||||
|
message: &message,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -485,7 +495,7 @@ mod tests {
|
|||||||
/// dialog the user has to actively dismiss, not a toast on its own timer.
|
/// dialog the user has to actively dismiss, not a toast on its own timer.
|
||||||
#[test]
|
#[test]
|
||||||
fn a_finished_import_answers_with_a_dismissible_dialog_not_a_toast() {
|
fn a_finished_import_answers_with_a_dismissible_dialog_not_a_toast() {
|
||||||
let html = render_success(Locale::English, 500, 500, "customers");
|
let html = render_success(Locale::English, 500, 500, "customers", &[]);
|
||||||
|
|
||||||
assert!(!html.contains("Template error"), "{html}");
|
assert!(!html.contains("Template error"), "{html}");
|
||||||
assert!(html.contains(r#"role="dialog""#), "{html}");
|
assert!(html.contains(r#"role="dialog""#), "{html}");
|
||||||
@@ -497,4 +507,19 @@ mod tests {
|
|||||||
assert!(!html.contains("$dispatch('notify'"), "{html}");
|
assert!(!html.contains("$dispatch('notify'"), "{html}");
|
||||||
assert!(html.contains("keydown.esc"), "{html}");
|
assert!(html.contains("keydown.esc"), "{html}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_finished_import_displays_non_blocking_warnings() {
|
||||||
|
let html = render_success(
|
||||||
|
Locale::English,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
"customers",
|
||||||
|
&["CSV row 2 (customers): Deleted row omitted required column 'name'".to_string()],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(html.contains("Warnings:"), "{html}");
|
||||||
|
assert!(html.contains("CSV row 2"), "{html}");
|
||||||
|
assert!(html.contains("Deleted row omitted required column"), "{html}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user