world_tax/
calculation_test.rs

1#[cfg(test)]
2mod tests {
3    use crate::{
4        Region, TaxDatabase, TaxScenario, TaxType, TradeAgreementOverride, TransactionType, VatRate,
5    };
6    use rust_decimal_macros::dec;
7
8    fn setup() -> TaxDatabase {
9        let _ = env_logger::builder()
10            .is_test(true)
11            .filter_level(log::LevelFilter::Debug) // Set to Debug level
12            .try_init();
13        TaxDatabase::from_files("vat_rates.json", "trade_agreements.json")
14            .expect("Tax database should load")
15    }
16
17    #[test]
18    fn test_german_vat_calculation() {
19        let db = setup();
20        let scenario = TaxScenario::new(
21            Region::new("DE".to_string(), None).expect("Valid German region"),
22            Region::new("DE".to_string(), None).expect("Valid German region"),
23            TransactionType::B2C,
24        );
25        let tax = scenario
26            .calculate_tax(100.0, &db)
27            .expect("Tax calculation should succeed");
28        assert_eq!(tax, 19.0); // Germany's actual VAT rate
29
30        let rates = scenario
31            .get_rates(100.0, &db)
32            .expect("Rates should be found");
33        assert_eq!(rates.len(), 1);
34        assert_eq!(rates[0].rate, 0.19);
35        assert_eq!(rates[0].tax_type, TaxType::VAT(VatRate::Standard));
36        assert!(!rates[0].compound);
37    }
38
39    #[test]
40    fn test_canadian_gst_bc_pst_below_threshold() {
41        let db = setup();
42        let scenario = TaxScenario::new(
43            Region::new("CA".to_string(), Some("CA-BC".to_string()))
44                .expect("Valid Canadian BC region"),
45            Region::new("CA".to_string(), Some("CA-BC".to_string()))
46                .expect("Valid Canadian BC region"),
47            TransactionType::B2C,
48        );
49
50        let tax = scenario
51            .calculate_tax(100.0, &db)
52            .expect("Tax calculation should succeed");
53        assert_eq!(tax, 0.0); // Combined GST (5%) + PST (7%) for British Columbia
54    }
55
56    #[test]
57    fn test_canadian_gst_bc_pst_ignore_threshold() {
58        let db = setup();
59        let mut scenario = TaxScenario::new(
60            Region::new("CA".to_string(), Some("CA-BC".to_string()))
61                .expect("Valid Canadian BC region"),
62            Region::new("CA".to_string(), Some("CA-BC".to_string()))
63                .expect("Valid Canadian BC region"),
64            TransactionType::B2C,
65        );
66        scenario.ignore_threshold = true;
67
68        let tax = scenario
69            .calculate_tax(100.0, &db)
70            .expect("Tax calculation should succeed");
71        assert_eq!(tax, 12.35); // Combined GST (5%) + PST (7%) for British Columbia
72    }
73
74    #[test]
75    fn test_canadian_gst_bc_pst_above_threshold() {
76        let db = setup();
77        let scenario = TaxScenario::new(
78            Region::new("CA".to_string(), Some("CA-BC".to_string()))
79                .expect("Valid Canadian BC region"),
80            Region::new("CA".to_string(), Some("CA-BC".to_string()))
81                .expect("Valid Canadian BC region"),
82            TransactionType::B2C,
83        );
84
85        let tax = scenario
86            .calculate_tax(100000.0, &db)
87            .expect("Tax calculation should succeed");
88        assert_eq!(tax, 12350.0); // Combined GST (5%) + PST (7%) for British Columbia
89    }
90
91    #[test]
92    fn test_eu_cross_border_b2b() {
93        let db = setup();
94        let scenario = TaxScenario::new(
95            Region::new("DE".to_string(), None).expect("Valid German region"),
96            Region::new("FR".to_string(), None).expect("Valid French region"),
97            TransactionType::B2B,
98        );
99
100        let tax = scenario
101            .calculate_tax(100.0, &db)
102            .expect("Tax calculation should succeed");
103        assert_eq!(tax, 0.0); // EU reverse charge mechanism
104    }
105
106    #[test]
107    fn test_eu_cross_border_b2c_digital() {
108        let db = setup();
109        let mut scenario = TaxScenario::new(
110            Region::new("DE".to_string(), None).expect("Valid German region"),
111            Region::new("FR".to_string(), None).expect("Valid French region"),
112            TransactionType::B2B,
113        );
114        scenario.is_digital_product_or_service = true;
115
116        let tax = scenario
117            .calculate_tax(100.0, &db)
118            .expect("Tax calculation should succeed");
119        assert_eq!(tax, 0.0); // EU reverse charge mechanism
120    }
121
122    #[test]
123    fn test_eu_cross_border_b2c() {
124        let db = setup();
125        let scenario = TaxScenario::new(
126            Region::new("DE".to_string(), None).expect("Valid German region"),
127            Region::new("FR".to_string(), None).expect("Valid French region"),
128            TransactionType::B2C,
129        );
130
131        let tax = scenario
132            .calculate_tax(100.0, &db)
133            .expect("Tax calculation should succeed");
134        assert_eq!(tax, 19.0); // EU reverse charge mechanism
135    }
136
137    #[test]
138    fn test_french_reduced_vat() {
139        let db = setup();
140        let mut scenario = TaxScenario::new(
141            Region::new("FR".to_string(), None).expect("Valid French region"),
142            Region::new("FR".to_string(), None).expect("Valid French region"),
143            TransactionType::B2C,
144        );
145        scenario.vat_rate = Some(VatRate::ReducedAlt);
146
147        let tax = scenario
148            .calculate_tax(100.0, &db)
149            .expect("Tax calculation should succeed");
150        assert_eq!(tax, 5.5); // France's actual reduced VAT rate
151    }
152
153    #[test]
154    fn test_german_domestic_b2b() {
155        let db = setup();
156        let scenario = TaxScenario::new(
157            Region::new("DE".to_string(), None).expect("Valid German region"),
158            Region::new("DE".to_string(), None).expect("Valid German region"),
159            TransactionType::B2B,
160        );
161
162        let tax = scenario
163            .calculate_tax(100.0, &db)
164            .expect("Tax calculation should succeed");
165        assert_eq!(tax, 19.0); // German domestic B2B VAT rate
166    }
167
168    #[test]
169    fn test_germany_thailand_b2b() {
170        let db = setup();
171        let scenario = TaxScenario::new(
172            Region::new("DE".to_string(), None).expect("Valid German region"),
173            Region::new("TH".to_string(), None).expect("Valid Thai region"),
174            TransactionType::B2B,
175        );
176
177        let tax = scenario
178            .calculate_tax(100.0, &db)
179            .expect("Tax calculation should succeed");
180        assert_eq!(tax, 0.0); // Export from EU is zero-rated
181    }
182
183    #[test]
184    fn test_germany_thailand_b2c() {
185        let db = setup();
186        let scenario = TaxScenario::new(
187            Region::new("DE".to_string(), None).expect("Valid German region"),
188            Region::new("TH".to_string(), None).expect("Valid Thai region"),
189            TransactionType::B2C,
190        );
191
192        let tax = scenario
193            .calculate_tax(100.0, &db)
194            .expect("Tax calculation should succeed");
195        assert_eq!(tax, 0.0); // Export from EU to non-EU country is zero-rated for B2C too
196    }
197
198    #[test]
199    fn test_us_interstate_b2c_below_threshold() {
200        let db = setup();
201        let scenario = TaxScenario::new(
202            Region::new("US".to_string(), Some("US-CA".to_string())).expect("Valid US-CA region"),
203            Region::new("US".to_string(), Some("US-WA".to_string())).expect("Valid US-WA region"),
204            TransactionType::B2C,
205        );
206
207        let tax = scenario
208            .calculate_tax(100.0, &db)
209            .expect("Tax calculation should succeed");
210        assert_eq!(tax, 0.0); // Washington state sales tax rate for remote sellers
211    }
212
213    #[test]
214    fn test_us_interstate_b2c_ignore_threshold() {
215        let db = setup();
216        let mut scenario = TaxScenario::new(
217            Region::new("US".to_string(), Some("US-CA".to_string())).expect("Valid US-CA region"),
218            Region::new("US".to_string(), Some("US-WA".to_string())).expect("Valid US-WA region"),
219            TransactionType::B2C,
220        );
221        scenario.ignore_threshold = true;
222
223        let tax = scenario
224            .calculate_tax(100.0, &db)
225            .expect("Tax calculation should succeed");
226        assert_eq!(tax, 6.5); // Washington state sales tax rate for remote sellers
227    }
228
229    #[test]
230    fn test_us_interstate_b2c_above_threshold() {
231        let db = setup();
232        let scenario = TaxScenario::new(
233            Region::new("US".to_string(), Some("US-CA".to_string())).expect("Valid US-CA region"),
234            Region::new("US".to_string(), Some("US-WA".to_string())).expect("Valid US-WA region"),
235            TransactionType::B2C,
236        );
237
238        let tax = scenario
239            .calculate_tax(100000.0, &db)
240            .expect("Tax calculation should succeed");
241        assert_eq!(tax, 6500.0); // Washington state sales tax rate for remote sellers
242    }
243
244    #[test]
245    fn test_us_interstate_b2b() {
246        let db = setup();
247        let mut scenario = TaxScenario::new(
248            Region::new("US".to_string(), Some("US-TX".to_string())).expect("Valid US-TX region"),
249            Region::new("US".to_string(), Some("US-WA".to_string())).expect("Valid US-WA region"),
250            TransactionType::B2B,
251        );
252        scenario.has_resale_certificate = true;
253
254        let tax = scenario
255            .calculate_tax(100.0, &db)
256            .expect("Tax calculation should succeed");
257        assert_eq!(tax, 0.0);
258    }
259
260    #[test]
261    fn test_us_interstate_b2b_reseller() {
262        let db = setup();
263        let mut scenario = TaxScenario::new(
264            Region::new("US".to_string(), Some("US-WA".to_string())).expect("Valid US-WA region"),
265            Region::new("US".to_string(), Some("US-TX".to_string())).expect("Valid US-TX region"),
266            TransactionType::B2B,
267        );
268        scenario.has_resale_certificate = true;
269
270        let tax = scenario
271            .calculate_tax(100.0, &db)
272            .expect("Tax calculation should succeed");
273        assert_eq!(tax, 0.0);
274    }
275
276    #[test]
277    fn test_gcc_cross_border_b2b() {
278        let db = setup();
279        let scenario = TaxScenario::new(
280            Region::new("AE".to_string(), None).expect("Valid UAE region"),
281            Region::new("QA".to_string(), None).expect("Valid Qatar region"),
282            TransactionType::B2B,
283        );
284
285        let tax = scenario
286            .calculate_tax(100.0, &db)
287            .expect("Tax calculation should succeed");
288        assert_eq!(tax, 0.0); // GCC countries have no VAT
289    }
290
291    #[test]
292    fn test_gcc_cross_border_b2c() {
293        let db = setup();
294        let scenario = TaxScenario::new(
295            Region::new("AE".to_string(), None).expect("Valid UAE region"),
296            Region::new("QA".to_string(), None).expect("Valid Qatar region"),
297            TransactionType::B2C,
298        );
299
300        let tax = scenario
301            .calculate_tax(100.0, &db)
302            .expect("Tax calculation should succeed");
303        assert_eq!(tax, 5.0); // GCC countries have no VAT
304    }
305
306    #[test]
307    fn test_gcc_cross_border_b2c_manual() {
308        let db = setup();
309        let scenario = TaxScenario {
310            source_region: Region::new("AE".to_string(), None).expect("Valid UAE region"),
311            destination_region: Region::new("QA".to_string(), None).expect("Valid Qatar region"),
312            transaction_type: TransactionType::B2C,
313            trade_agreement_override: None,
314            is_digital_product_or_service: false,
315            has_resale_certificate: false,
316            ignore_threshold: false,
317            vat_rate: None,
318        };
319
320        let tax = scenario
321            .calculate_tax(100.0, &db)
322            .expect("Tax calculation should succeed");
323        assert_eq!(tax, 5.0); // GCC countries have no VAT
324    }
325
326    #[test]
327    fn test_gcc_cross_border_b2c_manual_no_agreement() {
328        let db = setup();
329        let scenario = TaxScenario {
330            source_region: Region::new("AE".to_string(), None).expect("Valid UAE region"),
331            destination_region: Region::new("QA".to_string(), None).expect("Valid Qatar region"),
332            transaction_type: TransactionType::B2C,
333            trade_agreement_override: Some(TradeAgreementOverride::NoAgreement),
334            is_digital_product_or_service: false,
335            has_resale_certificate: false,
336            ignore_threshold: false,
337            vat_rate: None,
338        };
339
340        let tax = scenario
341            .calculate_tax(100.0, &db)
342            .expect("Tax calculation should succeed");
343        assert_eq!(tax, 0.0); // GCC countries have no VAT
344    }
345
346    #[test]
347    fn test_canadian_quebec_gst_qst() {
348        let db = setup();
349        let scenario = TaxScenario::new(
350            Region::new("CA".to_string(), Some("CA-QC".to_string()))
351                .expect("Valid Canadian QC region"),
352            Region::new("CA".to_string(), Some("CA-QC".to_string()))
353                .expect("Valid Canadian QC region"),
354            TransactionType::B2C,
355        );
356
357        let rates = scenario
358            .get_rates(100.0, &db)
359            .expect("Rates should be found");
360        assert_eq!(rates.len(), 2); // Should have both GST and QST
361
362        let gst_rate = rates
363            .iter()
364            .find(|r| matches!(r.tax_type, TaxType::GST))
365            .expect("Should have GST");
366        assert_eq!(gst_rate.rate, 0.05); // 5% GST
367
368        let qst_rate = rates
369            .iter()
370            .find(|r| matches!(r.tax_type, TaxType::QST))
371            .expect("Should have QST");
372        assert_eq!(qst_rate.rate, 0.09975); // 9.975% QST
373        assert!(qst_rate.compound); // QST should compound on GST
374    }
375
376    #[test]
377    fn test_canadian_nova_scotia_hst() {
378        let db = setup();
379        let scenario = TaxScenario::new(
380            Region::new("CA".to_string(), Some("CA-NS".to_string()))
381                .expect("Valid Canadian NS region"),
382            Region::new("CA".to_string(), Some("CA-NS".to_string()))
383                .expect("Valid Canadian NS region"),
384            TransactionType::B2C,
385        );
386
387        let rates = scenario
388            .get_rates(100.0, &db)
389            .expect("Rates should be found");
390        assert_eq!(rates.len(), 1); // Should only have HST
391        assert_eq!(rates[0].tax_type, TaxType::HST);
392        assert_eq!(rates[0].rate, 0.10); // Nova Scotia HST rate 10%
393        assert!(!rates[0].compound); // HST should not compound
394    }
395
396    #[test]
397    fn test_eu_zero_rate() {
398        let db = setup();
399        let mut scenario = TaxScenario::new(
400            Region::new("IE".to_string(), None).expect("Valid Irish region"),
401            Region::new("IE".to_string(), None).expect("Valid Irish region"),
402            TransactionType::B2C,
403        );
404        scenario.vat_rate = Some(VatRate::Zero);
405
406        let tax = scenario
407            .calculate_tax(100.0, &db)
408            .expect("Tax calculation should succeed");
409        assert_eq!(tax, 0.0); // Zero-rated goods in Ireland
410    }
411
412    #[test]
413    fn test_multiple_tax_rates() {
414        let db = setup();
415        let scenario = TaxScenario::new(
416            Region::new("CA".to_string(), Some("CA-BC".to_string()))
417                .expect("Valid Canadian BC region"),
418            Region::new("CA".to_string(), Some("CA-BC".to_string()))
419                .expect("Valid Canadian BC region"),
420            TransactionType::B2C,
421        );
422
423        let rates = scenario
424            .get_rates(100000.0, &db)
425            .expect("Rates should be found");
426        assert_eq!(rates.len(), 2); // Should have both GST and PST
427        assert!(rates.iter().any(|r| matches!(r.tax_type, TaxType::GST)));
428        assert!(rates.iter().any(|r| matches!(r.tax_type, TaxType::PST)));
429    }
430
431    #[test]
432    fn test_reverse_charge_vat() {
433        let db = setup();
434        let mut scenario = TaxScenario::new(
435            Region::new("DE".to_string(), None).expect("Valid German region"),
436            Region::new("FR".to_string(), None).expect("Valid French region"),
437            TransactionType::B2B,
438        );
439        scenario.vat_rate = Some(VatRate::ReverseCharge);
440
441        let rates = scenario
442            .get_rates(100.0, &db)
443            .expect("Rates should be found");
444        assert_eq!(rates.len(), 1);
445        assert_eq!(rates[0].rate, 0.0);
446        assert!(matches!(
447            rates[0].tax_type,
448            TaxType::VAT(VatRate::ReverseCharge)
449        ));
450    }
451
452    #[test]
453    fn test_us_state_no_sales_tax() {
454        let db = setup();
455        let scenario = TaxScenario::new(
456            Region::new("US".to_string(), Some("US-OR".to_string())).expect("Valid US-OR region"),
457            Region::new("US".to_string(), Some("US-OR".to_string())).expect("Valid US-OR region"),
458            TransactionType::B2C,
459        );
460
461        let tax = scenario
462            .calculate_tax(100.0, &db)
463            .expect("Tax calculation should succeed");
464        assert_eq!(tax, 0.0); // Oregon has no sales tax
465    }
466
467    #[test]
468    fn test_us_states_get_rates() {
469        let db = setup();
470        let mut scenario = TaxScenario::new(
471            Region::new("US".to_string(), Some("US-AS".to_string())).expect("Valid US-AK region"),
472            Region::new("US".to_string(), Some("US-CA".to_string())).expect("Valid US-CA region"),
473            TransactionType::B2C,
474        );
475        scenario.ignore_threshold = true;
476
477        let rates = scenario.get_rates(1.0, &db).expect("Rates should be found");
478        assert_eq!(rates.len(), 1);
479        assert_eq!(rates[0].rate, 0.0825); // California sales tax rate
480    }
481
482    #[test]
483    fn test_specific_trade_agreement() {
484        // let db = setup();
485        let mut scenario = TaxScenario::new(
486            Region::new("DE".to_string(), None).expect("Valid German region"),
487            Region::new("FR".to_string(), None).expect("Valid French region"),
488            TransactionType::B2C,
489        );
490        scenario.trade_agreement_override =
491            Some(TradeAgreementOverride::UseAgreement("EU".to_string()));
492
493        // let tax = scenario.calculate_tax(100.0, &db).expect("Tax calculation should succeed");
494        // Assert based on EU agreement rules
495    }
496
497    #[test]
498    fn test_exempt_vat_rate() {
499        let db = setup();
500        let mut scenario = TaxScenario::new(
501            Region::new("GB".to_string(), None).expect("Valid UK region"),
502            Region::new("GB".to_string(), None).expect("Valid UK region"),
503            TransactionType::B2C,
504        );
505        scenario.vat_rate = Some(VatRate::Exempt);
506
507        let rates = scenario
508            .get_rates(100.0, &db)
509            .expect("Rates should be found");
510        assert_eq!(rates.len(), 1);
511        assert_eq!(rates[0].rate, 0.0);
512        assert!(matches!(rates[0].tax_type, TaxType::VAT(VatRate::Exempt)));
513    }
514
515    #[test]
516    fn test_decimal_german_vat_calculation() {
517        let db = setup();
518        let scenario = TaxScenario::new(
519            Region::new("DE".to_string(), None).expect("Valid German region"),
520            Region::new("DE".to_string(), None).expect("Valid German region"),
521            TransactionType::B2C,
522        );
523
524        let tax = scenario
525            .calculate_tax_decimal(dec!(100.00), &db)
526            .expect("Tax calculation should succeed");
527        assert_eq!(tax, dec!(19.00)); // Germany's VAT rate with precise decimal calculation
528    }
529
530    #[test]
531    fn test_decimal_multiple_compound_calculations() {
532        let db = setup();
533        let scenario = TaxScenario::new(
534            Region::new("CA".to_string(), Some("CA-QC".to_string()))
535                .expect("Valid Canadian QC region"),
536            Region::new("CA".to_string(), Some("CA-QC".to_string()))
537                .expect("Valid Canadian QC region"),
538            TransactionType::B2C,
539        );
540
541        let amount = dec!(7999999.99);
542        let decimal_tax = scenario
543            .calculate_tax_decimal(amount, &db)
544            .expect("Decimal tax calculation should succeed");
545        let float_tax = scenario
546            .calculate_tax(7999999.99, &db)
547            .expect("Float tax calculation should succeed");
548
549        assert_eq!(decimal_tax, dec!(1237899.998452625));
550        assert_eq!(float_tax, 1237900.0); // Should show difference from float calculation
551    }
552
553    #[test]
554    fn load_included_db() {
555        let _ = TaxDatabase::new();
556    }
557}