jquants_api_client/api/
financial_statement_details.rs

1//! Financial Statement Data(BS/PL) (/fins/fs_details) API.
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6use super::{
7    shared::{
8        traits::{
9            builder::JQuantsBuilder,
10            pagination::{HasPaginationKey, MergePage, Paginatable},
11        },
12        types::type_of_document::TypeOfDocument,
13    },
14    JQuantsApiClient, JQuantsPlanClient,
15};
16
17/// Builder for Financial Statement Details Data API.
18#[derive(Clone, Serialize)]
19pub struct FinancialStatementDetailsBuilder {
20    #[serde(skip)]
21    client: JQuantsApiClient,
22
23    /// Issue code (e.g. "27890" or "2789")
24    #[serde(skip_serializing_if = "Option::is_none")]
25    code: Option<String>,
26    /// Disclosure date (e.g. "20210901" or "2021-09-01")
27    #[serde(skip_serializing_if = "Option::is_none")]
28    date: Option<String>,
29
30    /// Pagination key.
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pagination_key: Option<String>,
33}
34
35impl JQuantsBuilder<FinancialStatementDetailsResponse> for FinancialStatementDetailsBuilder {
36    async fn send(self) -> Result<FinancialStatementDetailsResponse, crate::JQuantsError> {
37        self.send_ref().await
38    }
39
40    async fn send_ref(&self) -> Result<FinancialStatementDetailsResponse, crate::JQuantsError> {
41        self.client.inner.get("fins/fs_details", self).await
42    }
43}
44
45impl Paginatable<FinancialStatementDetailsResponse> for FinancialStatementDetailsBuilder {
46    fn pagination_key(mut self, pagination_key: impl Into<String>) -> Self {
47        self.pagination_key = Some(pagination_key.into());
48        self
49    }
50}
51
52impl FinancialStatementDetailsBuilder {
53    /// Create a new builder.
54    pub(crate) fn new(client: JQuantsApiClient) -> Self {
55        Self {
56            client,
57            code: None,
58            date: None,
59            pagination_key: None,
60        }
61    }
62
63    /// Set issue code (e.g. "27890" or "2789")
64    pub fn code(mut self, code: impl Into<String>) -> Self {
65        self.code = Some(code.into());
66        self
67    }
68
69    /// Set disclosure date (e.g. "20210901" or "2021-09-01")
70    pub fn date(mut self, date: impl Into<String>) -> Self {
71        self.date = Some(date.into());
72        self
73    }
74}
75
76/// Trait for Financial Statement Details Data API.
77pub trait FinancialStatementDetailsApi: JQuantsPlanClient {
78    /// Get API builder for Financial Statement Details Data.
79    ///
80    /// Use [Financial Statement Details Data (/fins/fs_details) API](https://jpx.gitbook.io/j-quants-en/api-reference/statements-1)
81    fn get_financial_statement_details(&self) -> FinancialStatementDetailsBuilder {
82        FinancialStatementDetailsBuilder::new(self.get_api_client().clone())
83    }
84}
85
86/// Financial Statement Details Data response.
87///
88/// See: [API Reference](https://jpx.gitbook.io/j-quants-en/api-reference/statements-1)
89#[derive(Debug, Clone, PartialEq, Deserialize)]
90pub struct FinancialStatementDetailsResponse {
91    /// List of financial statement details
92    pub fs_details: Vec<FinancialStatementDetailItem>,
93    /// Pagination key for fetching next set of data
94    pub pagination_key: Option<String>,
95}
96
97impl HasPaginationKey for FinancialStatementDetailsResponse {
98    fn get_pagination_key(&self) -> Option<&str> {
99        self.pagination_key.as_deref()
100    }
101}
102
103impl MergePage for FinancialStatementDetailsResponse {
104    fn merge_page(
105        page: Result<Vec<Self>, crate::JQuantsError>,
106    ) -> Result<Self, crate::JQuantsError> {
107        let mut page = page?;
108        let mut merged = page.pop().unwrap();
109        for p in page {
110            merged.fs_details.extend(p.fs_details);
111        }
112        merged.pagination_key = None;
113
114        Ok(merged)
115    }
116}
117
118/// Represents a single financial statement detail item.
119#[derive(Debug, Clone, PartialEq, Deserialize)]
120pub struct FinancialStatementDetailItem {
121    /// Disclosed Date (YYYY-MM-DD)
122    #[serde(rename = "DisclosedDate")]
123    pub disclosed_date: String,
124
125    /// Disclosed Time (HH:MM:SS)
126    #[serde(rename = "DisclosedTime")]
127    pub disclosed_time: String,
128
129    /// Issue Code (5-character)
130    #[serde(rename = "LocalCode")]
131    pub local_code: String,
132
133    /// Disclosure Number
134    ///
135    /// Ascending order by disclosure number.
136    #[serde(rename = "DisclosureNumber")]
137    pub disclosure_number: String,
138
139    /// Type of Document
140    #[serde(rename = "TypeOfDocument")]
141    pub type_of_document: TypeOfDocument,
142
143    /// Financial Statement Entries
144    ///
145    /// Redundant labels (English) associated with XBRL tags and their values
146    #[serde(rename = "FinancialStatement")]
147    pub financial_statement: HashMap<String, String>,
148}
149
150#[cfg(test)]
151mod tests {
152    use maplit::hashmap;
153
154    use super::*;
155
156    #[test]
157    fn test_deserialize_financial_statement_details_response() {
158        let json_data = r#"
159        {
160            "fs_details": [
161                {
162                      "DisclosedDate": "2023-01-30",
163                      "DisclosedTime": "12:00:00",
164                      "LocalCode": "86970",
165                      "DisclosureNumber": "20230127594871",
166                      "TypeOfDocument": "3QFinancialStatements_Consolidated_IFRS",
167                      "FinancialStatement": {
168                            "Goodwill (IFRS)": "67374000000",
169                            "Retained earnings (IFRS)": "263894000000",
170                            "Operating profit (loss) (IFRS)": "51765000000.0",
171                            "Previous fiscal year end date, DEI": "2022-03-31",
172                            "Basic earnings (loss) per share (IFRS)": "66.76",
173                            "Document type, DEI": "四半期第3号参考様式 [IFRS](連結)",
174                            "Current period end date, DEI": "2022-12-31",
175                            "Revenue - 2 (IFRS)": "100987000000.0",
176                            "Industry code when consolidated financial statements are prepared in accordance with industry specific regulations, DEI": "CTE",
177                            "Profit (loss) attributable to owners of parent (IFRS)": "35175000000.0",
178                            "Other current liabilities - CL (IFRS)": "8904000000",
179                            "Share of profit (loss) of investments accounted for using equity method (IFRS)": "1042000000.0",
180                            "Current liabilities (IFRS)": "78852363000000",
181                            "Equity attributable to owners of parent (IFRS)": "311103000000",
182                            "Whether consolidated financial statements are prepared, DEI": "true",
183                            "Non-current liabilities (IFRS)": "33476000000",
184                            "Other expenses (IFRS)": "58000000.0",
185                            "Income taxes payable - CL (IFRS)": "5245000000",
186                            "Filer name in English, DEI": "Japan Exchange Group, Inc.",
187                            "Non-controlling interests (IFRS)": "8918000000",
188                            "Capital surplus (IFRS)": "38844000000",
189                            "Finance costs (IFRS)": "71000000.0",
190                            "Other current assets - CA (IFRS)": "4217000000",
191                            "Property, plant and equipment (IFRS)": "11277000000",
192                            "Deferred tax liabilities (IFRS)": "419000000",
193                            "Other components of equity (IFRS)": "422000000",
194                            "Current fiscal year start date, DEI": "2022-04-01",
195                            "Type of current period, DEI": "Q3",
196                            "Cash and cash equivalents (IFRS)": "91135000000",
197                            "Share capital (IFRS)": "11500000000",
198                            "Retirement benefit asset - NCA (IFRS)": "9028000000",
199                            "Number of submission, DEI": "1",
200                            "Trade and other receivables - CA (IFRS)": "18837000000",
201                            "Liabilities and equity (IFRS)": "79205861000000",
202                            "EDINET code, DEI": "E03814",
203                            "Equity (IFRS)": "320021000000",
204                            "Security code, DEI": "86970",
205                            "Other financial assets - CA (IFRS)": "112400000000",
206                            "Other financial assets - NCA (IFRS)": "2898000000",
207                            "Income taxes receivable - CA (IFRS)": "5529000000",
208                            "Investments accounted for using equity method (IFRS)": "18362000000",
209                            "Other non-current assets - NCA (IFRS)": "6240000000",
210                            "Previous fiscal year start date, DEI": "2021-04-01",
211                            "Filer name in Japanese, DEI": "株式会社日本取引所グループ",
212                            "Deferred tax assets (IFRS)": "2862000000",
213                            "Trade and other payables - CL (IFRS)": "5037000000",
214                            "Bonds and borrowings - CL (IFRS)": "33000000000",
215                            "Current fiscal year end date, DEI": "2023-03-31",
216                            "XBRL amendment flag, DEI": "false",
217                            "Non-current assets (IFRS)": "182317000000",
218                            "Retirement benefit liability - NCL (IFRS)": "9214000000",
219                            "Amendment flag, DEI": "false",
220                            "Assets (IFRS)": "79205861000000",
221                            "Income tax expense (IFRS)": "15841000000.0",
222                            "Report amendment flag, DEI": "false",
223                            "Profit (loss) (IFRS)": "35894000000.0",
224                            "Operating expenses (IFRS)": "50206000000.0",
225                            "Intangible assets (IFRS)": "36324000000",
226                            "Profit (loss) before tax from continuing operations (IFRS)": "51736000000.0",
227                            "Liabilities (IFRS)": "78885839000000",
228                            "Accounting standards, DEI": "IFRS",
229                            "Bonds and borrowings - NCL (IFRS)": "19972000000",
230                            "Finance income (IFRS)": "43000000.0",
231                            "Profit (loss) attributable to non-controlling interests (IFRS)": "719000000.0",
232                            "Comparative period end date, DEI": "2021-12-31",
233                            "Current assets (IFRS)": "79023543000000",
234                            "Other non-current liabilities - NCL (IFRS)": "3870000000",
235                            "Other income (IFRS)": "458000000.0",
236                            "Treasury shares (IFRS)": "-3556000000"
237                      }
238                }
239            ],
240            "pagination_key": "value1.value2."
241        }
242        "#;
243
244        let response: FinancialStatementDetailsResponse = serde_json::from_str(json_data).unwrap();
245        let financial_statement_map: HashMap<&str, &str> = hashmap! {
246            "Goodwill (IFRS)" => "67374000000",
247            "Retained earnings (IFRS)" => "263894000000",
248            "Operating profit (loss) (IFRS)" => "51765000000.0",
249            "Previous fiscal year end date, DEI" => "2022-03-31",
250            "Basic earnings (loss) per share (IFRS)" => "66.76",
251            "Document type, DEI" => "四半期第3号参考様式 [IFRS](連結)",
252            "Current period end date, DEI" => "2022-12-31",
253            "Revenue - 2 (IFRS)" => "100987000000.0",
254            "Industry code when consolidated financial statements are prepared in accordance with industry specific regulations, DEI" => "CTE",
255            "Profit (loss) attributable to owners of parent (IFRS)" => "35175000000.0",
256            "Other current liabilities - CL (IFRS)" => "8904000000",
257            "Share of profit (loss) of investments accounted for using equity method (IFRS)" => "1042000000.0",
258            "Current liabilities (IFRS)" => "78852363000000",
259            "Equity attributable to owners of parent (IFRS)" => "311103000000",
260            "Whether consolidated financial statements are prepared, DEI" => "true",
261            "Non-current liabilities (IFRS)" => "33476000000",
262            "Other expenses (IFRS)" => "58000000.0",
263            "Income taxes payable - CL (IFRS)" => "5245000000",
264            "Filer name in English, DEI" => "Japan Exchange Group, Inc.",
265            "Non-controlling interests (IFRS)" => "8918000000",
266            "Capital surplus (IFRS)" => "38844000000",
267            "Finance costs (IFRS)" => "71000000.0",
268            "Other current assets - CA (IFRS)" => "4217000000",
269            "Property, plant and equipment (IFRS)" => "11277000000",
270            "Deferred tax liabilities (IFRS)" => "419000000",
271            "Other components of equity (IFRS)" => "422000000",
272            "Current fiscal year start date, DEI" => "2022-04-01",
273            "Type of current period, DEI" =>  "Q3",
274            "Cash and cash equivalents (IFRS)" => "91135000000",
275            "Share capital (IFRS)" => "11500000000",
276            "Retirement benefit asset - NCA (IFRS)" => "9028000000",
277            "Number of submission, DEI" =>  "1",
278            "Trade and other receivables - CA (IFRS)" => "18837000000",
279            "Liabilities and equity (IFRS)" => "79205861000000",
280            "EDINET code, DEI" =>  "E03814",
281            "Equity (IFRS)" =>  "320021000000",
282            "Security code, DEI" =>  "86970",
283            "Other financial assets - CA (IFRS)" => "112400000000",
284            "Other financial assets - NCA (IFRS)" => "2898000000",
285            "Income taxes receivable - CA (IFRS)" => "5529000000",
286            "Investments accounted for using equity method (IFRS)" => "18362000000",
287            "Other non-current assets - NCA (IFRS)" => "6240000000",
288            "Previous fiscal year start date, DEI" => "2021-04-01",
289            "Filer name in Japanese, DEI" => "株式会社日本取引所グループ",
290            "Deferred tax assets (IFRS)" => "2862000000",
291            "Trade and other payables - CL (IFRS)" => "5037000000",
292            "Bonds and borrowings - CL (IFRS)" => "33000000000",
293            "Current fiscal year end date, DEI" => "2023-03-31",
294            "XBRL amendment flag, DEI" =>  "false",
295            "Non-current assets (IFRS)" => "182317000000",
296            "Retirement benefit liability - NCL (IFRS)" => "9214000000",
297            "Amendment flag, DEI" =>  "false",
298            "Assets (IFRS)" =>  "79205861000000",
299            "Income tax expense (IFRS)" => "15841000000.0",
300            "Report amendment flag, DEI" => "false",
301            "Profit (loss) (IFRS)" => "35894000000.0",
302            "Operating expenses (IFRS)" => "50206000000.0",
303            "Intangible assets (IFRS)" => "36324000000",
304            "Profit (loss) before tax from continuing operations (IFRS)" => "51736000000.0",
305            "Liabilities (IFRS)" => "78885839000000",
306            "Accounting standards, DEI" =>  "IFRS",
307            "Bonds and borrowings - NCL (IFRS)" => "19972000000",
308            "Finance income (IFRS)" => "43000000.0",
309            "Profit (loss) attributable to non-controlling interests (IFRS)" => "719000000.0",
310            "Comparative period end date, DEI" => "2021-12-31",
311            "Current assets (IFRS)" => "79023543000000",
312            "Other non-current liabilities - NCL (IFRS)" => "3870000000",
313            "Other income (IFRS)" => "458000000.0",
314            "Treasury shares (IFRS)" => "-3556000000",
315        };
316
317        let expected_response = FinancialStatementDetailsResponse {
318            fs_details: vec![FinancialStatementDetailItem {
319                disclosed_date: "2023-01-30".to_string(),
320                disclosed_time: "12:00:00".to_string(),
321                local_code: "86970".to_string(),
322                disclosure_number: "20230127594871".to_string(),
323                type_of_document: TypeOfDocument::Q3FinancialStatementsConsolidatedIFRS,
324                financial_statement: financial_statement_map
325                    .into_iter()
326                    .map(|(k, v)| (k.to_string(), v.to_string()))
327                    .collect(),
328            }],
329            pagination_key: Some("value1.value2.".to_string()),
330        };
331
332        pretty_assertions::assert_eq!(response, expected_response);
333    }
334
335    #[test]
336    fn test_deserialize_financial_statement_details_response_no_pagination_key() {
337        let json_data = r#"
338        {
339            "fs_details": [
340                {
341                    "DisclosedDate": "2023-01-30",
342                    "DisclosedTime": "12:00:00",
343                    "LocalCode": "86970",
344                    "DisclosureNumber": "20230127594871",
345                    "TypeOfDocument": "3QFinancialStatements_Consolidated_IFRS",
346                    "FinancialStatement": {
347                        "Goodwill (IFRS)": "67374000000",
348                        "Retained earnings (IFRS)": "263894000000"
349                    }
350                }
351            ]
352        }
353        "#;
354
355        let response: FinancialStatementDetailsResponse = serde_json::from_str(json_data).unwrap();
356        let financial_statement_map: HashMap<&str, &str> = hashmap! {
357            "Goodwill (IFRS)" =>  "67374000000",
358            "Retained earnings (IFRS)" => "263894000000"
359        };
360
361        let expected_response = FinancialStatementDetailsResponse {
362            fs_details: vec![FinancialStatementDetailItem {
363                disclosed_date: "2023-01-30".to_string(),
364                disclosed_time: "12:00:00".to_string(),
365                local_code: "86970".to_string(),
366                disclosure_number: "20230127594871".to_string(),
367                type_of_document: TypeOfDocument::Q3FinancialStatementsConsolidatedIFRS,
368                financial_statement: financial_statement_map
369                    .into_iter()
370                    .map(|(k, v)| (k.to_string(), v.to_string()))
371                    .collect(),
372            }],
373            pagination_key: None,
374        };
375
376        pretty_assertions::assert_eq!(response, expected_response);
377    }
378
379    #[test]
380    fn test_deserialize_financial_statement_details_response_multiple_items() {
381        let json_data = r#"
382        {
383            "fs_details": [
384                {
385                    "DisclosedDate": "2023-01-30",
386                    "DisclosedTime": "12:00:00",
387                    "LocalCode": "86970",
388                    "DisclosureNumber": "20230127594871",
389                    "TypeOfDocument": "3QFinancialStatements_Consolidated_IFRS",
390                    "FinancialStatement": {
391                        "Goodwill (IFRS)": "67374000000",
392                        "Retained earnings (IFRS)": "263894000000"
393                    }
394                },
395                {
396                    "DisclosedDate": "2023-02-15",
397                    "DisclosedTime": "14:30:00",
398                    "LocalCode": "86971",
399                    "DisclosureNumber": "20230227594872",
400                    "TypeOfDocument": "OtherPeriodFinancialStatements_Consolidated_IFRS",
401                    "FinancialStatement": {
402                        "Goodwill (IFRS)": "70000000000",
403                        "Retained earnings (IFRS)": "280000000000"
404                    }
405                }
406            ],
407            "pagination_key": "value3.value4."
408        }
409        "#;
410
411        let response: FinancialStatementDetailsResponse = serde_json::from_str(json_data).unwrap();
412
413        let fs_map1: HashMap<&str, &str> = hashmap! {
414            "Goodwill (IFRS)" => "67374000000",
415            "Retained earnings (IFRS)" => "263894000000",
416            // ... other fields omitted for brevity
417        };
418
419        let fs_map2: HashMap<&str, &str> = hashmap! {
420            "Goodwill (IFRS)" => "70000000000",
421            "Retained earnings (IFRS)" => "280000000000"
422            // ... other fields omitted for brevity
423        };
424
425        let expected_response = FinancialStatementDetailsResponse {
426            fs_details: vec![
427                FinancialStatementDetailItem {
428                    disclosed_date: "2023-01-30".to_string(),
429                    disclosed_time: "12:00:00".to_string(),
430                    local_code: "86970".to_string(),
431                    disclosure_number: "20230127594871".to_string(),
432                    type_of_document: TypeOfDocument::Q3FinancialStatementsConsolidatedIFRS,
433                    financial_statement: fs_map1
434                        .into_iter()
435                        .map(|(k, v)| (k.to_string(), v.to_string()))
436                        .collect(),
437                },
438                FinancialStatementDetailItem {
439                    disclosed_date: "2023-02-15".to_string(),
440                    disclosed_time: "14:30:00".to_string(),
441                    local_code: "86971".to_string(),
442                    disclosure_number: "20230227594872".to_string(),
443                    type_of_document:
444                        TypeOfDocument::OtherPeriodFinancialStatementsConsolidatedIFRS,
445                    financial_statement: fs_map2
446                        .into_iter()
447                        .map(|(k, v)| (k.to_string(), v.to_string()))
448                        .collect(),
449                },
450            ],
451            pagination_key: Some("value3.value4.".to_string()),
452        };
453
454        pretty_assertions::assert_eq!(response, expected_response);
455    }
456
457    #[test]
458    fn test_deserialize_financial_statement_details_response_no_data() {
459        let json_data = r#"
460        {
461            "fs_details": []
462        }
463        "#;
464
465        let response: FinancialStatementDetailsResponse = serde_json::from_str(json_data).unwrap();
466        let expected_response = FinancialStatementDetailsResponse {
467            fs_details: vec![],
468            pagination_key: None,
469        };
470
471        pretty_assertions::assert_eq!(response, expected_response);
472    }
473}