From 0f1f5efe486d20e16d5622fdbb28a70000dd7434 Mon Sep 17 00:00:00 2001 From: Anton Oehler Date: Thu, 5 Dec 2024 15:49:09 +0100 Subject: [PATCH 1/3] Implement BR-14 and BR-CO-15 --- src/components/business_rules.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/components/business_rules.rs b/src/components/business_rules.rs index 2d0769d..fdd1018 100644 --- a/src/components/business_rules.rs +++ b/src/components/business_rules.rs @@ -24,7 +24,7 @@ const BUSINESS_RULES: &[fn(&Invoice) -> Result<(), BusinessRuleViolation>] = &[ // br_11, br_12, br_13, - // br_14, + br_14, // br_15, // br_16, // br_17, @@ -81,7 +81,7 @@ const BUSINESS_RULES: &[fn(&Invoice) -> Result<(), BusinessRuleViolation>] = &[ // br_co_12, br_co_13, br_co_14, - // br_co_15, + br_co_15, // br_co_16, br_co_17, // br_co_18, @@ -140,6 +140,14 @@ fn br_13(invoice: &Invoice) -> Result<(), BusinessRuleViolation> { br_109.discard_value().check(rule, "BR-109") } +/// BR-14: An Invoice shall have the Invoice total amount with VAT (BT-112) +fn br_14(invoice: &Invoice) -> Result<(), BusinessRuleViolation> { + let rule = ("BR-14", "An Invoice shall have the Invoice total amount with VAT (BT-112)."); + let br_112 = invoice.supply_chain_trade_transaction.applicable_header_trade_settlement.specified_trade_settlement_header_monetary_summation.grand_total_amount; + + br_112.discard_value().check(rule, "BR-112") +} + /// BR-CO-10: Sum of Invoice line net amount (BT-106) = ∑ Invoice line net amount (BT-131). fn br_co_10(invoice: &Invoice) -> Result<(), BusinessRuleViolation> { let rule = ("BR-CO-10", "Sum of Invoice line net amount (BT-106) = ∑ Invoice line net amount (BT-131)."); @@ -174,6 +182,17 @@ fn br_co_14(invoice: &Invoice) -> Result<(), BusinessRuleViolation> { check_float_eq!(rule; bt_110, bt_117_sum; bt_110, bt_117_sum) } +/// BR-CO-15: Invoice total amount with VAT (BT-112) = Invoice total amount without VAT (BT-109) + Invoice total VAT amount (BT-110). +fn br_co_15(invoice: &Invoice) -> Result<(), BusinessRuleViolation> { + let rule = ("BR-CO-15", "Invoice total amount with VAT (BT-112) = Invoice total amount without VAT (BT-109) + Invoice total VAT amount (BT-110)."); + let bt_109 = invoice.supply_chain_trade_transaction.applicable_header_trade_settlement.specified_trade_settlement_header_monetary_summation.tax_basis_total_amount.check(rule, "BT-109")?; + let bt_110 = invoice.supply_chain_trade_transaction.applicable_header_trade_settlement.specified_trade_settlement_header_monetary_summation.tax_total_amount.clone().check(rule, "BT-110")?.amount; + let bt_112 = invoice.supply_chain_trade_transaction.applicable_header_trade_settlement.specified_trade_settlement_header_monetary_summation.grand_total_amount.check(rule, "BT-112")?; + + check_float_eq!(rule; bt_112, bt_109 + bt_110; bt_112, bt_109, bt_110) +} + + /// BR-CO-17: VAT category tax amount (BT-117) = VAT category taxable amount (BT-116) x (VAT category rate (BT-119) / 100), rounded to two decimals fn br_co_17(invoice: &Invoice) -> Result<(), BusinessRuleViolation> { let rule = ("BR-CO-17", "VAT category tax amount (BT-117) = VAT category taxable amount (BT-116) x (VAT category rate (BT-119) / 100), rounded to two decimals"); From fb8f00048704a306a7ee6925e617feaccd80093f Mon Sep 17 00:00:00 2001 From: Anton Oehler Date: Thu, 5 Dec 2024 16:38:23 +0100 Subject: [PATCH 2/3] Implement BR-15 and BR-CO-16 --- src/components/business_rules.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/components/business_rules.rs b/src/components/business_rules.rs index fdd1018..fee537d 100644 --- a/src/components/business_rules.rs +++ b/src/components/business_rules.rs @@ -25,7 +25,7 @@ const BUSINESS_RULES: &[fn(&Invoice) -> Result<(), BusinessRuleViolation>] = &[ br_12, br_13, br_14, - // br_15, + br_15, // br_16, // br_17, // br_18, @@ -82,7 +82,7 @@ const BUSINESS_RULES: &[fn(&Invoice) -> Result<(), BusinessRuleViolation>] = &[ br_co_13, br_co_14, br_co_15, - // br_co_16, + br_co_16, br_co_17, // br_co_18, // br_co_19, @@ -148,6 +148,14 @@ fn br_14(invoice: &Invoice) -> Result<(), BusinessRuleViolation> { br_112.discard_value().check(rule, "BR-112") } +/// BR-15: An Invoice shall have the Amount due for payment (BT-115). +fn br_15(invoice: &Invoice) -> Result<(), BusinessRuleViolation> { + let rule = ("BR-15", "An Invoice shall have the Amount due for payment (BT-115)."); + let br_115 = invoice.supply_chain_trade_transaction.applicable_header_trade_settlement.specified_trade_settlement_header_monetary_summation.due_payable_amount; + + br_115.discard_value().check(rule, "BR-115") +} + /// BR-CO-10: Sum of Invoice line net amount (BT-106) = ∑ Invoice line net amount (BT-131). fn br_co_10(invoice: &Invoice) -> Result<(), BusinessRuleViolation> { let rule = ("BR-CO-10", "Sum of Invoice line net amount (BT-106) = ∑ Invoice line net amount (BT-131)."); @@ -192,6 +200,20 @@ fn br_co_15(invoice: &Invoice) -> Result<(), BusinessRuleViolation> { check_float_eq!(rule; bt_112, bt_109 + bt_110; bt_112, bt_109, bt_110) } +/// BR-CO-16: Amount due for payment (BT-115) = Invoice total amount with VAT (BT-112) - Paid amount (BT-113) + Rounding amount (BT-114). +fn br_co_16(invoice: &Invoice) -> Result<(), BusinessRuleViolation> { + let rule = ("BR-CO-16", "Amount due for payment (BT-115) = Invoice total amount with VAT (BT-112) - Paid amount (BT-113) + Rounding amount (BT-114)."); + let bt_112 = invoice.supply_chain_trade_transaction.applicable_header_trade_settlement.specified_trade_settlement_header_monetary_summation.grand_total_amount.check(rule, "BT-112")?; + // TODO: neither exists so far + // let bt_113 = invoice.supply_chain_trade_transaction.applicable_header_trade_settlement.specified_trade_settlement_header_monetary_summation.paid_amount.unwrap_or(0.0); + // let bt_114 = invoice.supply_chain_trade_transaction.applicable_header_trade_settlement.specified_trade_settlement_header_monetary_summation.rounding_amount.unwrap_or(0.0); + let bt_113 = 0.0; + let bt_114 = 0.0; + let bt_115 = invoice.supply_chain_trade_transaction.applicable_header_trade_settlement.specified_trade_settlement_header_monetary_summation.due_payable_amount.check(rule, "BT-115")?; + + check_float_eq!(rule; bt_115, bt_112 - bt_113 + bt_114; bt_115, bt_112, bt_113, bt_114) +} + /// BR-CO-17: VAT category tax amount (BT-117) = VAT category taxable amount (BT-116) x (VAT category rate (BT-119) / 100), rounded to two decimals fn br_co_17(invoice: &Invoice) -> Result<(), BusinessRuleViolation> { From 8255b3c94d762313700262a40f6bb441f25a2ada Mon Sep 17 00:00:00 2001 From: Anton Oehler Date: Fri, 6 Dec 2024 17:13:35 +0100 Subject: [PATCH 3/3] Implement BR-CO-4 --- src/components/business_rules.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/components/business_rules.rs b/src/components/business_rules.rs index fee537d..574af64 100644 --- a/src/components/business_rules.rs +++ b/src/components/business_rules.rs @@ -70,7 +70,7 @@ const BUSINESS_RULES: &[fn(&Invoice) -> Result<(), BusinessRuleViolation>] = &[ // br_64, // br_65, // br_co_03, - // br_co_04, + br_co_04, // br_co_05, // br_co_06, // br_co_07, @@ -156,6 +156,23 @@ fn br_15(invoice: &Invoice) -> Result<(), BusinessRuleViolation> { br_115.discard_value().check(rule, "BR-115") } +/// BR-CO-4: Each Invoice line (BG-25) shall be categorized with an Invoiced item VAT category code (BT-151). +fn br_co_04(invoice: &Invoice) -> Result<(), BusinessRuleViolation> { + // Should never fail because structure is enforced, but maybe something changes in the future + let rule = ("BR-CO-4", "Each Invoice line (BG-25) shall be categorized with an Invoiced item VAT category code (BT-151)."); + for line in &invoice.supply_chain_trade_transaction.included_supply_chain_trade_line_items { + if line.specified_line_trade_settlement.applicable_trade_tax.category_code.as_str().is_empty() { + return Err(BusinessRuleViolation { + rule_id: rule.0.to_string(), + rule_text: rule.1.to_string(), + message: "VAT category code is missing".to_string(), + fields: vec![("line_id".to_string(), line.associated_document_line_document.line_id.to_string())], + }); + } + } + Ok(()) +} + /// BR-CO-10: Sum of Invoice line net amount (BT-106) = ∑ Invoice line net amount (BT-131). fn br_co_10(invoice: &Invoice) -> Result<(), BusinessRuleViolation> { let rule = ("BR-CO-10", "Sum of Invoice line net amount (BT-106) = ∑ Invoice line net amount (BT-131).");