web aliasing of accounting and others

This commit is contained in:
Priec
2026-08-13 19:05:41 +02:00
parent 6c15fcedc8
commit 8662b00ccf
7 changed files with 161 additions and 33 deletions

View File

@@ -231,7 +231,15 @@ impl AddTablePageState {
// backend names and remain the exception.
let aliasable = column.data_type != ACCOUNTING_TRANSFER_FIELD_TYPE;
for generated in columns.generated_columns_of(index) {
let generated_columns = columns.generated_columns_of(index);
// Whether the backend already reports `account` as one of them, in
// which case the foreign-key row below is a second view of a column
// that is named once, not a column of its own to name again.
let account_reported = generated_columns
.iter()
.any(|generated| generated.name == ACCOUNT_API_COLUMN);
for generated in generated_columns {
let mut tags = vec![format!("generated by {}", column.data_type)];
if generated.inherits_currency {
tags.push(format!("{}, {}", column.currency, column.money_mode.label()));
@@ -250,9 +258,10 @@ impl AddTablePageState {
});
}
// The account foreign key is a system column rather than a
// generated user column, so the catalog does not report it; the
// same explanation as in `TableDraft::preview_rows`.
// The physical column the account foreign key is stored in. It is
// a system column, so it is listed whether or not the catalog
// reports `account` among the generated ones — but it is named
// there when it does, never twice.
if column.data_type == ACCOUNTING_FIELD_TYPE {
rows.push(ColumnRow {
index: None,
@@ -269,7 +278,7 @@ impl AddTablePageState {
self.draft.generated_display_name(ACCOUNT_API_COLUMN)
),
],
alias_source: Some(ACCOUNT_API_COLUMN.to_string()),
alias_source: (!account_reported).then(|| ACCOUNT_API_COLUMN.to_string()),
alias: self.draft.alias_for(ACCOUNT_API_COLUMN).to_string(),
});
}
@@ -277,6 +286,33 @@ impl AddTablePageState {
rows
}
/// The generated columns that may be renamed, in the order they are
/// listed.
///
/// Renaming lives in a section of its own rather than in the column list:
/// the list is where the table is read, and a text box in every other row
/// of it turns reading into scanning past inputs.
/// One field per generated column and no more: a source named twice would
/// be two fields over one column, and the draft reads the first of them,
/// so whatever was typed in the second would be dropped without a word.
pub(crate) fn alias_rows(&self) -> Vec<AliasRow> {
let mut rows: Vec<AliasRow> = Vec::new();
for row in self.column_rows() {
let Some(source) = row.alias_source else {
continue;
};
if rows.iter().any(|existing| existing.source == source) {
continue;
}
rows.push(AliasRow {
source,
alias: row.alias,
data_type: row.data_type,
});
}
rows
}
/// Row-display candidates: `id` first, then every column, matching the
/// client's candidate list.
pub(crate) fn row_display_candidates(&self) -> Vec<RowDisplayCandidate> {
@@ -331,6 +367,16 @@ pub(crate) struct ColumnRow {
pub alias: String,
}
/// One line of the "Rename generated columns" section: a generated column the
/// request may name, and the name asked for it so far.
pub(crate) struct AliasRow {
/// The generated column's own name, which is what the rename asks for.
pub source: String,
/// The alias typed for it, empty when the backend's name is being kept.
pub alias: String,
pub data_type: String,
}
pub(crate) struct RowDisplayCandidate {
pub index: usize,
pub name: String,
@@ -449,6 +495,7 @@ mod tests {
"tax_point_date",
"debit",
"credit",
"account",
"account_id",
]
);
@@ -504,7 +551,11 @@ mod tests {
let row = |name: &str| rows.iter().find(|row| row.name == name).unwrap();
assert_eq!(row("debit").alias_source.as_deref(), Some("debit"));
assert_eq!(row("debit").alias, "md");
assert_eq!(row("account_id").alias_source.as_deref(), Some("account"));
// `account` is reported by the catalog, so it is named there and the
// physical column it lands in is not offered a second field.
assert_eq!(row("account").alias_source.as_deref(), Some("account"));
assert_eq!(row("account").alias, "ucet");
assert!(row("account_id").alias_source.is_none());
assert!(row("account_id").tags.contains(&"written as ucet".to_string()));
assert!(
row("accounting").alias_source.is_none(),