diff --git a/.gitignore b/.gitignore index e9e86ec1..28eb681b 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ mutants*/ testing/ run_bins/ result +corporate_erp/ diff --git a/client b/client index 74ebcab6..bcd298b3 160000 --- a/client +++ b/client @@ -1 +1 @@ -Subproject commit 74ebcab618be649ce9ceb2c7fb2e56fbc891428c +Subproject commit bcd298b3f1a9930e71f3c65caa3fa4a1361737f5 diff --git a/common/proto/tables_data.proto b/common/proto/tables_data.proto index edad66df..a71f3881 100644 --- a/common/proto/tables_data.proto +++ b/common/proto/tables_data.proto @@ -418,6 +418,10 @@ message GetTableDataResponse { // Active journal containing this row's automatic accounting line, when the // table is ACCOUNTING-enabled and the row has been posted. optional int64 journal_id = 6; + + // Present when the row was loaded through a form-navigation RPC. The row and + // its numbering bounds come from the same server operation. + RowNavigation navigation = 7; } // Count non-deleted rows. @@ -433,13 +437,34 @@ message GetTableDataCountRequest { message GetLastTableDataRequest { string profile_name = 1; string table_name = 2; + RowNavigationMode navigation_mode = 3; +} + +// Controls how an unfiltered form numbers rows. +enum RowNavigationMode { + // Actual database row id / highest visible row id. Deleted ids leave gaps. + ROW_NAVIGATION_MODE_ID = 0; + // Ordinal position / exact visible-row count. Deleted rows are omitted. + ROW_NAVIGATION_MODE_POSITION = 1; +} + +// Complete numbering state returned with a navigated row. +message RowNavigation { + RowNavigationMode mode = 1; + // Actual row id in ID mode; 1-based ordinal in POSITION mode. For an empty + // table this is the first new-row slot (1). + uint64 current = 2; + // Highest visible id in ID mode; exact visible-row count in POSITION mode. + uint64 total = 3; } message GetLastTableDataResponse { - // Zero when the table has no visible rows. + // Zero when the table has no visible rows. Retained as row identity; clients + // should use navigation for display bounds. int64 last_id = 1; // Absent when last_id is zero. GetTableDataResponse row = 2; + RowNavigation navigation = 3; } enum RowIdDirection { @@ -452,11 +477,15 @@ message GetAdjacentTableDataRequest { string table_name = 2; int64 anchor_id = 3; RowIdDirection direction = 4; + RowNavigationMode navigation_mode = 5; + // Current ordinal position when navigation_mode is POSITION. Ignored in ID mode. + uint64 anchor_position = 6; } message GetAdjacentTableDataResponse { // Absent when no visible row exists in the requested direction. GetTableDataResponse row = 1; + RowNavigation navigation = 2; } // Fetch by ordinal position among non-deleted rows (1-based). @@ -467,6 +496,10 @@ message GetTableDataByPositionRequest { // Required. Table name within the profile. string table_name = 2; - // Required. 1-based position by id ascending among rows with deleted = FALSE. + // Set this or record_id, never both. This is the 1-based position by id + // ascending among rows with deleted = FALSE. int32 position = 3; + // Direct row lookup that also returns its ID or ordinal navigation bounds. + int64 record_id = 4; + RowNavigationMode navigation_mode = 5; } diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index bdf7cb64..e4081779 100644 Binary files a/common/src/proto/descriptor.bin and b/common/src/proto/descriptor.bin differ diff --git a/common/src/proto/komp_ac.tables_data.rs b/common/src/proto/komp_ac.tables_data.rs index d259b25c..cc3e2b70 100644 --- a/common/src/proto/komp_ac.tables_data.rs +++ b/common/src/proto/komp_ac.tables_data.rs @@ -376,6 +376,10 @@ pub struct GetTableDataResponse { /// table is ACCOUNTING-enabled and the row has been posted. #[prost(int64, optional, tag = "6")] pub journal_id: ::core::option::Option, + /// Present when the row was loaded through a form-navigation RPC. The row and + /// its numbering bounds come from the same server operation. + #[prost(message, optional, tag = "7")] + pub navigation: ::core::option::Option, } /// Count non-deleted rows. #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] @@ -394,15 +398,33 @@ pub struct GetLastTableDataRequest { pub profile_name: ::prost::alloc::string::String, #[prost(string, tag = "2")] pub table_name: ::prost::alloc::string::String, + #[prost(enumeration = "RowNavigationMode", tag = "3")] + pub navigation_mode: i32, +} +/// Complete numbering state returned with a navigated row. +#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] +pub struct RowNavigation { + #[prost(enumeration = "RowNavigationMode", tag = "1")] + pub mode: i32, + /// Actual row id in ID mode; 1-based ordinal in POSITION mode. For an empty + /// table this is the first new-row slot (1). + #[prost(uint64, tag = "2")] + pub current: u64, + /// Highest visible id in ID mode; exact visible-row count in POSITION mode. + #[prost(uint64, tag = "3")] + pub total: u64, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct GetLastTableDataResponse { - /// Zero when the table has no visible rows. + /// Zero when the table has no visible rows. Retained as row identity; clients + /// should use navigation for display bounds. #[prost(int64, tag = "1")] pub last_id: i64, /// Absent when last_id is zero. #[prost(message, optional, tag = "2")] pub row: ::core::option::Option, + #[prost(message, optional, tag = "3")] + pub navigation: ::core::option::Option, } #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct GetAdjacentTableDataRequest { @@ -414,12 +436,19 @@ pub struct GetAdjacentTableDataRequest { pub anchor_id: i64, #[prost(enumeration = "RowIdDirection", tag = "4")] pub direction: i32, + #[prost(enumeration = "RowNavigationMode", tag = "5")] + pub navigation_mode: i32, + /// Current ordinal position when navigation_mode is POSITION. Ignored in ID mode. + #[prost(uint64, tag = "6")] + pub anchor_position: u64, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct GetAdjacentTableDataResponse { /// Absent when no visible row exists in the requested direction. #[prost(message, optional, tag = "1")] pub row: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub navigation: ::core::option::Option, } /// Fetch by ordinal position among non-deleted rows (1-based). #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] @@ -430,9 +459,15 @@ pub struct GetTableDataByPositionRequest { /// Required. Table name within the profile. #[prost(string, tag = "2")] pub table_name: ::prost::alloc::string::String, - /// Required. 1-based position by id ascending among rows with deleted = FALSE. + /// Set this or record_id, never both. This is the 1-based position by id + /// ascending among rows with deleted = FALSE. #[prost(int32, tag = "3")] pub position: i32, + /// Direct row lookup that also returns its ID or ordinal navigation bounds. + #[prost(int64, tag = "4")] + pub record_id: i64, + #[prost(enumeration = "RowNavigationMode", tag = "5")] + pub navigation_mode: i32, } /// A link resolved exactly as the source row stored it, together with the /// target row's current state. The physical companion version column remains @@ -474,6 +509,35 @@ impl ResolvedTableLinkStatus { } } } +/// Controls how an unfiltered form numbers rows. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum RowNavigationMode { + /// Actual database row id / highest visible row id. Deleted ids leave gaps. + Id = 0, + /// Ordinal position / exact visible-row count. Deleted rows are omitted. + Position = 1, +} +impl RowNavigationMode { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Id => "ROW_NAVIGATION_MODE_ID", + Self::Position => "ROW_NAVIGATION_MODE_POSITION", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "ROW_NAVIGATION_MODE_ID" => Some(Self::Id), + "ROW_NAVIGATION_MODE_POSITION" => Some(Self::Position), + _ => None, + } + } +} #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] pub enum RowIdDirection { diff --git a/server b/server index 32cab246..c958eb0f 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit 32cab246fa68c29eae7759bdde7cfd567b24c6f5 +Subproject commit c958eb0f17080b86c97d487974ecba46467eaa71 diff --git a/web/src/pages/import_export/export/logic.rs b/web/src/pages/import_export/export/logic.rs index c39f65df..f864857e 100644 --- a/web/src/pages/import_export/export/logic.rs +++ b/web/src/pages/import_export/export/logic.rs @@ -11,7 +11,9 @@ use crate::{ AppState, definitions::{ table_structure::GetTableStructureRequest, - tables_data::{GetTableDataByPositionRequest, GetTableDataCountRequest}, + tables_data::{ + GetTableDataByPositionRequest, GetTableDataCountRequest, RowNavigationMode, + }, }, {i18n::Locale, tr}, services::{authenticated_request, reject_cross_site}, @@ -156,6 +158,8 @@ pub(crate) async fn export_csv( profile_name: profile_name.clone(), table_name: table.name.clone(), position: position as i32, + record_id: 0, + navigation_mode: RowNavigationMode::Position as i32, }; let mut data = state.tables_data.clone(); let response = match data.get_table_data_by_position(match authenticated_request(&headers, request) {