1mod builder;
3mod qr;
4pub mod sign;
5pub mod validation;
6pub mod xml;
7pub use builder::{FinalizedInvoice, InvoiceBuilder, InvoiceView, SignedInvoice};
8pub use qr::{QrCodeError, QrPayload, QrResult};
9
10#[allow(unused_imports)]
11use bitflags::bitflags;
12use chrono::{NaiveDate, NaiveDateTime};
13use iso_currency::Currency as IsoCurrency;
14use isocountry::CountryCode as IsoCountryCode;
15use std::marker::PhantomData;
16use std::str::FromStr;
17use thiserror::Error;
18use serde::{Deserialize, Serialize};
19
20type Result<T> = std::result::Result<T, InvoiceError>;
21
22#[derive(Debug, Error)]
24pub enum InvoiceError {
25 #[error(transparent)]
26 Validation(#[from] ValidationError),
27 #[error("Invalid country code: {0}")]
28 InvalidCountryCode(String),
29 #[error("Invalid currency code: {0}")]
30 InvalidCurrencyCode(String),
31 #[error("Invalid invoice timestamp: {0}")]
32 InvalidTimestamp(String),
33 #[error("Invalid invoice date: {0}")]
34 InvalidIssueDate(String),
35 #[error("Missing VAT ID for seller")]
36 MissingVatForSeller,
37 #[error("Missing Buyer ID for buyer")]
38 MissingBuyerId,
39 #[error("Invalid VAT ID format")]
40 InvalidVatFormat,
41}
42
43#[derive(Debug, Clone, PartialEq, Eq, Hash, Error)]
45#[error("invoice validation failed")]
46pub struct ValidationError {
47 issues: Vec<ValidationIssue>,
48}
49
50impl ValidationError {
51 pub fn new(issues: Vec<ValidationIssue>) -> Self {
52 Self { issues }
53 }
54
55 pub fn issues(&self) -> &[ValidationIssue] {
56 &self.issues
57 }
58}
59
60#[derive(Debug, Clone, PartialEq, Eq, Hash)]
62pub struct ValidationIssue {
63 field: InvoiceField,
64 kind: ValidationKind,
65 line_item_index: Option<usize>,
66}
67
68impl ValidationIssue {
69 pub fn new(
70 field: InvoiceField,
71 kind: ValidationKind,
72 line_item_index: Option<usize>,
73 ) -> Self {
74 Self {
75 field,
76 kind,
77 line_item_index,
78 }
79 }
80
81 pub fn field(&self) -> InvoiceField {
82 self.field
83 }
84
85 pub fn kind(&self) -> ValidationKind {
86 self.kind
87 }
88
89 pub fn line_item_index(&self) -> Option<usize> {
90 self.line_item_index
91 }
92}
93
94#[non_exhaustive]
95#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
97pub enum InvoiceField {
98 Id,
99 Uuid,
100 IssueDateTime,
101 Currency,
102 PreviousInvoiceHash,
103 InvoiceCounter,
104 Seller,
105 LineItems,
106 PaymentMeansCode,
107 VatCategory,
108 LineItemDescription,
109 LineItemUnitCode,
110 LineItemQuantity,
111 LineItemUnitPrice,
112 LineItemTotalAmount,
113 LineItemVatRate,
114 LineItemVatAmount,
115}
116
117#[non_exhaustive]
118#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
120pub enum ValidationKind {
121 Missing,
122 Empty,
123 InvalidFormat,
124 OutOfRange,
125 Mismatch,
126}
127
128#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
130pub struct CountryCode(String);
131
132impl CountryCode {
133 pub fn parse<S: Into<String>>(s: S) -> Result<Self> {
134 let value = s.into().trim().to_uppercase();
135 let normalized = match value.len() {
136 2 => IsoCountryCode::for_alpha2(&value)
137 .map_err(|_| InvoiceError::InvalidCountryCode(value.clone()))?
138 .alpha3()
139 .to_string(),
140 3 => IsoCountryCode::for_alpha3(&value)
141 .map_err(|_| InvoiceError::InvalidCountryCode(value.clone()))?
142 .alpha3()
143 .to_string(),
144 _ => return Err(InvoiceError::InvalidCountryCode(value)),
145 };
146 Ok(Self(normalized))
147 }
148
149 pub fn as_str(&self) -> &str {
150 &self.0
151 }
152
153 pub(crate) fn alpha2(&self) -> String {
154 IsoCountryCode::for_alpha3(self.as_str())
155 .expect("validated country code")
156 .alpha2()
157 .to_string()
158 }
159}
160
161impl AsRef<str> for CountryCode {
162 fn as_ref(&self) -> &str {
163 self.as_str()
164 }
165}
166
167impl FromStr for CountryCode {
168 type Err = InvoiceError;
169 fn from_str(s: &str) -> Result<Self> {
170 CountryCode::parse(s)
171 }
172}
173
174impl TryFrom<String> for CountryCode {
175 type Error = InvoiceError;
176 fn try_from(value: String) -> Result<Self> {
177 CountryCode::parse(value)
178 }
179}
180
181impl TryFrom<&str> for CountryCode {
182 type Error = InvoiceError;
183 fn try_from(value: &str) -> Result<Self> {
184 CountryCode::parse(value)
185 }
186}
187
188#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
190pub struct CurrencyCode(String);
191
192impl CurrencyCode {
193 pub fn parse<S: Into<String>>(s: S) -> Result<Self> {
194 let value = s.into().trim().to_uppercase();
195 IsoCurrency::from_code(&value)
196 .ok_or_else(|| InvoiceError::InvalidCurrencyCode(value.clone()))?;
197 Ok(Self(value))
198 }
199
200 pub fn as_str(&self) -> &str {
201 &self.0
202 }
203}
204
205impl AsRef<str> for CurrencyCode {
206 fn as_ref(&self) -> &str {
207 self.as_str()
208 }
209}
210
211impl FromStr for CurrencyCode {
212 type Err = InvoiceError;
213 fn from_str(s: &str) -> Result<Self> {
214 CurrencyCode::parse(s)
215 }
216}
217
218impl TryFrom<String> for CurrencyCode {
219 type Error = InvoiceError;
220 fn try_from(value: String) -> Result<Self> {
221 CurrencyCode::parse(value)
222 }
223}
224
225impl TryFrom<&str> for CurrencyCode {
226 type Error = InvoiceError;
227 fn try_from(value: &str) -> Result<Self> {
228 CurrencyCode::parse(value)
229 }
230}
231
232#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
234pub struct InvoiceTimestamp(String);
235
236impl InvoiceTimestamp {
237 pub fn parse<S: Into<String>>(s: S) -> Result<Self> {
238 let value = s.into().trim().to_string();
239 let parsed = NaiveDateTime::parse_from_str(&value, "%Y-%m-%dT%H:%M:%SZ")
240 .map_err(|_| InvoiceError::InvalidTimestamp(value.clone()))?;
241 Ok(Self(parsed.format("%Y-%m-%dT%H:%M:%SZ").to_string()))
242 }
243
244 pub fn as_str(&self) -> &str {
245 &self.0
246 }
247
248 pub(crate) fn date_str(&self) -> &str {
249 &self.0[..10]
250 }
251
252 pub(crate) fn time_str(&self) -> &str {
253 &self.0[11..19]
254 }
255}
256
257impl AsRef<str> for InvoiceTimestamp {
258 fn as_ref(&self) -> &str {
259 self.as_str()
260 }
261}
262
263impl FromStr for InvoiceTimestamp {
264 type Err = InvoiceError;
265 fn from_str(s: &str) -> Result<Self> {
266 InvoiceTimestamp::parse(s)
267 }
268}
269
270impl TryFrom<String> for InvoiceTimestamp {
271 type Error = InvoiceError;
272 fn try_from(value: String) -> Result<Self> {
273 InvoiceTimestamp::parse(value)
274 }
275}
276
277impl TryFrom<&str> for InvoiceTimestamp {
278 type Error = InvoiceError;
279 fn try_from(value: &str) -> Result<Self> {
280 InvoiceTimestamp::parse(value)
281 }
282}
283
284#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
286pub struct InvoiceDate(String);
287
288impl InvoiceDate {
289 pub fn parse<S: Into<String>>(s: S) -> Result<Self> {
290 let value = s.into().trim().to_string();
291 let parsed = NaiveDate::parse_from_str(&value, "%Y-%m-%d")
292 .map_err(|_| InvoiceError::InvalidIssueDate(value.clone()))?;
293 Ok(Self(parsed.format("%Y-%m-%d").to_string()))
294 }
295
296 pub fn as_str(&self) -> &str {
297 &self.0
298 }
299}
300
301impl AsRef<str> for InvoiceDate {
302 fn as_ref(&self) -> &str {
303 self.as_str()
304 }
305}
306
307impl FromStr for InvoiceDate {
308 type Err = InvoiceError;
309 fn from_str(s: &str) -> Result<Self> {
310 InvoiceDate::parse(s)
311 }
312}
313
314impl TryFrom<String> for InvoiceDate {
315 type Error = InvoiceError;
316 fn try_from(value: String) -> Result<Self> {
317 InvoiceDate::parse(value)
318 }
319}
320
321impl TryFrom<&str> for InvoiceDate {
322 type Error = InvoiceError;
323 fn try_from(value: &str) -> Result<Self> {
324 InvoiceDate::parse(value)
325 }
326}
327
328#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
330pub struct Address {
331 pub country_code: CountryCode,
332 pub city: String,
333 pub street: String,
334 pub additional_street: Option<String>,
335 pub building_number: String,
336 pub additional_number: Option<String>,
337 pub postal_code: String, pub subdivision: Option<String>,
339 pub district: Option<String>,
340}
341
342impl Address {
343 pub fn country_code(&self) -> &CountryCode {
344 &self.country_code
345 }
346
347 pub fn city(&self) -> &str {
348 &self.city
349 }
350
351 pub fn street(&self) -> &str {
352 &self.street
353 }
354
355 pub fn additional_street(&self) -> Option<&str> {
356 self.additional_street.as_deref()
357 }
358
359 pub fn building_number(&self) -> &str {
360 &self.building_number
361 }
362
363 pub fn additional_number(&self) -> Option<&str> {
364 self.additional_number.as_deref()
365 }
366
367 pub fn postal_code(&self) -> &str {
368 &self.postal_code
369 }
370
371 pub fn subdivision(&self) -> Option<&str> {
372 self.subdivision.as_deref()
373 }
374
375 pub fn district(&self) -> Option<&str> {
376 self.district.as_deref()
377 }
378}
379
380#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
394pub struct VatId(String);
395impl VatId {
396 pub fn parse<S: Into<String>>(s: S) -> Result<Self> {
397 let s = s.into().trim().to_string();
398 if s.is_empty() {
399 return Err(InvoiceError::InvalidVatFormat);
400 }
401 Ok(VatId(s))
403 }
404 pub fn as_str(&self) -> &str {
405 &self.0
406 }
407}
408impl AsRef<str> for VatId {
409 fn as_ref(&self) -> &str {
410 self.as_str()
411 }
412}
413impl FromStr for VatId {
414 type Err = InvoiceError;
415 fn from_str(s: &str) -> Result<Self> {
416 VatId::parse(s)
417 }
418}
419impl TryFrom<String> for VatId {
420 type Error = InvoiceError;
421 fn try_from(value: String) -> Result<Self> {
422 VatId::parse(value)
423 }
424}
425impl TryFrom<&str> for VatId {
426 type Error = InvoiceError;
427 fn try_from(value: &str) -> Result<Self> {
428 VatId::parse(value)
429 }
430}
431
432#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
443pub struct OtherId {
444 value: String,
445 scheme_id: Option<String>,
446}
447impl OtherId {
448 pub fn new<S: Into<String>>(value: S) -> Self {
449 OtherId {
450 value: value.into(),
451 scheme_id: None,
452 }
453 }
454
455 pub fn with_scheme<V: Into<String>, S: Into<String>>(value: V, scheme_id: S) -> Self {
456 OtherId {
457 value: value.into(),
458 scheme_id: Some(scheme_id.into()),
459 }
460 }
461
462 pub fn as_str(&self) -> &str {
463 &self.value
464 }
465
466 pub fn scheme_id(&self) -> Option<&str> {
467 self.scheme_id.as_deref()
468 }
469}
470impl AsRef<str> for OtherId {
471 fn as_ref(&self) -> &str {
472 self.as_str()
473 }
474}
475
476#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
487pub struct InvoiceNote {
488 language: String,
489 text: String,
490}
491
492impl InvoiceNote {
493 pub fn new(language: impl Into<String>, text: impl Into<String>) -> Self {
494 Self {
495 language: language.into(),
496 text: text.into(),
497 }
498 }
499
500 pub fn language(&self) -> &str {
501 &self.language
502 }
503
504 pub fn text(&self) -> &str {
505 &self.text
506 }
507}
508
509pub trait PartyRole {}
512
513#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
515pub struct SellerRole;
516impl PartyRole for SellerRole {}
517#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
519pub struct BuyerRole;
520impl PartyRole for BuyerRole {}
521
522#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
550#[allow(dead_code)]
551pub struct Party<R: PartyRole> {
552 _marker: PhantomData<R>,
553 name: String,
554 address: Address,
555 vat_id: Option<VatId>,
556 other_id: Option<OtherId>,
557}
558
559pub type Seller = Party<SellerRole>;
560pub type Buyer = Party<BuyerRole>;
561
562impl Party<SellerRole> {
563 pub fn new(
568 name: String,
569 address: Address,
570 vat_id: impl Into<String>, other_id: Option<OtherId>, ) -> Result<Self> {
573 let vat = VatId::parse(vat_id.into())?;
574 Ok(Party {
575 _marker: PhantomData,
576 name,
577 address,
578 vat_id: Some(vat),
579 other_id,
580 })
581 }
582}
583
584impl Party<BuyerRole> {
585 pub fn new(
590 name: String,
591 address: Address,
592 vat_id: Option<String>, other_id: Option<OtherId>, ) -> Result<Self> {
595 let vat = match vat_id {
596 Some(v) => Some(VatId::parse(v)?),
597 None => None,
598 };
599 if vat.is_none() && other_id.is_none() {
600 return Err(InvoiceError::MissingBuyerId);
601 }
602 Ok(Party {
603 _marker: PhantomData,
604 name,
605 address,
606 vat_id: vat,
607 other_id,
608 })
609 }
610}
611
612impl<R: PartyRole> Party<R> {
613 pub fn name(&self) -> &str {
614 &self.name
615 }
616
617 pub fn address(&self) -> &Address {
618 &self.address
619 }
620
621 pub fn vat_id(&self) -> Option<&VatId> {
622 self.vat_id.as_ref()
623 }
624
625 pub fn other_id(&self) -> Option<&OtherId> {
626 self.other_id.as_ref()
627 }
628}
629
630#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
640pub enum InvoiceSubType {
641 Simplified,
642 Standard,
643}
644
645#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
657pub struct OriginalInvoiceRef {
658 id: String,
659 uuid: Option<String>,
660 issue_date: Option<InvoiceDate>,
661}
662
663impl OriginalInvoiceRef {
664 pub fn new(id: impl Into<String>) -> Self {
665 Self {
666 id: id.into(),
667 uuid: None,
668 issue_date: None,
669 }
670 }
671
672 pub fn with_uuid(mut self, uuid: impl Into<String>) -> Self {
673 self.uuid = Some(uuid.into());
674 self
675 }
676
677 pub fn with_issue_date(mut self, issue_date: InvoiceDate) -> Self {
678 self.issue_date = Some(issue_date);
679 self
680 }
681
682 pub fn with_issue_date_str(mut self, issue_date: impl Into<String>) -> Result<Self> {
683 self.issue_date = Some(InvoiceDate::parse(issue_date)?);
684 Ok(self)
685 }
686
687 pub fn id(&self) -> &str {
688 &self.id
689 }
690
691 pub fn uuid(&self) -> Option<&str> {
692 self.uuid.as_deref()
693 }
694
695 pub fn issue_date(&self) -> Option<&InvoiceDate> {
696 self.issue_date.as_ref()
697 }
698}
699
700#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
710pub enum InvoiceType {
711 Tax(InvoiceSubType),
712 Prepayment(InvoiceSubType),
713 CreditNote(InvoiceSubType, OriginalInvoiceRef, String), DebitNote(InvoiceSubType, OriginalInvoiceRef, String), }
716
717impl InvoiceType {
718 pub fn is_simplified(&self) -> bool {
719 matches!(
720 self,
721 InvoiceType::Tax(InvoiceSubType::Simplified)
722 | InvoiceType::Prepayment(InvoiceSubType::Simplified)
723 | InvoiceType::CreditNote(InvoiceSubType::Simplified, ..)
724 | InvoiceType::DebitNote(InvoiceSubType::Simplified, ..)
725 )
726 }
727}
728
729#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
739pub enum VatCategory {
740 Exempt,
741 Standard,
742 Zero,
743 OutOfScope,
744}
745#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
755pub struct LineItem {
756 description: String,
757 quantity: f64,
758 unit_code: String,
759 unit_price: f64,
760 total_amount: f64,
761 vat_rate: f64,
762 vat_amount: f64,
763 vat_category: VatCategory,
764}
765
766impl LineItem {
767 pub fn new(
768 description: impl Into<String>,
769 quantity: f64,
770 unit_code: impl Into<String>,
771 unit_price: f64,
772 vat_rate: f64,
773 vat_category: VatCategory,
774 ) -> Self {
775 let total_amount = Self::calculate_total_amount(quantity, unit_price);
776 let vat_amount = Self::calculate_vat_amount(total_amount, vat_rate);
777 Self {
778 description: description.into(),
779 quantity,
780 unit_code: unit_code.into(),
781 unit_price,
782 total_amount,
783 vat_rate,
784 vat_amount,
785 vat_category,
786 }
787 }
788
789 pub fn from_totals(
790 description: impl Into<String>,
791 quantity: f64,
792 unit_code: impl Into<String>,
793 unit_price: f64,
794 total_amount: f64,
795 vat_rate: f64,
796 vat_category: VatCategory,
797 ) -> Self {
798 let vat_amount = Self::calculate_vat_amount(total_amount, vat_rate);
799 Self {
800 description: description.into(),
801 quantity,
802 unit_code: unit_code.into(),
803 unit_price,
804 total_amount,
805 vat_rate,
806 vat_amount,
807 vat_category,
808 }
809 }
810
811 pub fn try_from_parts(
816 description: impl Into<String>,
817 quantity: f64,
818 unit_code: impl Into<String>,
819 unit_price: f64,
820 total_amount: f64,
821 vat_rate: f64,
822 vat_amount: f64,
823 vat_category: VatCategory,
824 ) -> std::result::Result<Self, ValidationError> {
825 const EPSILON: f64 = 0.01;
826 let expected_total = Self::calculate_total_amount(quantity, unit_price);
827 let expected_vat = Self::calculate_vat_amount(total_amount, vat_rate);
828
829 let mut issues = Vec::new();
830 if (expected_total - total_amount).abs() > EPSILON {
831 issues.push(ValidationIssue::new(
832 InvoiceField::LineItemTotalAmount,
833 ValidationKind::Mismatch,
834 None,
835 ));
836 }
837 if (expected_vat - vat_amount).abs() > EPSILON {
838 issues.push(ValidationIssue::new(
839 InvoiceField::LineItemVatAmount,
840 ValidationKind::Mismatch,
841 None,
842 ));
843 }
844 if !issues.is_empty() {
845 return Err(ValidationError::new(issues));
846 }
847
848 Ok(Self {
849 description: description.into(),
850 quantity,
851 unit_code: unit_code.into(),
852 unit_price,
853 total_amount,
854 vat_rate,
855 vat_amount,
856 vat_category,
857 })
858 }
859
860 pub fn description(&self) -> &str {
861 &self.description
862 }
863
864 pub fn quantity(&self) -> f64 {
865 self.quantity
866 }
867
868 pub fn unit_code(&self) -> &str {
869 &self.unit_code
870 }
871
872 pub fn unit_price(&self) -> f64 {
873 self.unit_price
874 }
875
876 pub fn total_amount(&self) -> f64 {
877 self.total_amount
878 }
879
880 pub fn vat_rate(&self) -> f64 {
881 self.vat_rate
882 }
883
884 pub fn vat_amount(&self) -> f64 {
885 self.vat_amount
886 }
887
888 pub fn vat_category(&self) -> VatCategory {
889 self.vat_category
890 }
891
892 fn calculate_total_amount(quantity: f64, unit_price: f64) -> f64 {
893 quantity * unit_price
894 }
895
896 fn calculate_vat_amount(total_amount: f64, vat_rate: f64) -> f64 {
897 total_amount * (vat_rate / 100.0)
898 }
899}
900
901pub type LineItems = Vec<LineItem>;
918
919bitflags! {
920 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
930 pub struct InvoiceFlags: u8 {
931 const THIRD_PARTY = 0b00001;
932 const NOMINAL = 0b00010;
933 const EXPORT = 0b00100;
934 const SUMMARY = 0b01000;
935 const SELF_BILLED = 0b10000;
936 }
937}
938
939#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
951pub struct InvoiceData {
952 invoice_type: InvoiceType,
953 id: String,
954 uuid: String,
955 issue_datetime: InvoiceTimestamp,
956 currency: CurrencyCode, previous_invoice_hash: String,
958 invoice_counter: u64,
959 note: Option<InvoiceNote>,
960 seller: Seller,
961 buyer: Option<Buyer>,
962 line_items: LineItems,
963 payment_means_code: String,
964 vat_category: VatCategory,
965
966 flags: InvoiceFlags,
967
968 invoice_level_charge: f64,
969 invoice_level_discount: f64,
970 allowance_reason: Option<String>,
971}
972
973impl InvoiceData {
974 pub fn invoice_type(&self) -> &InvoiceType {
975 &self.invoice_type
976 }
977
978 pub fn id(&self) -> &str {
979 &self.id
980 }
981
982 pub fn uuid(&self) -> &str {
983 &self.uuid
984 }
985
986 pub fn issue_datetime(&self) -> &InvoiceTimestamp {
987 &self.issue_datetime
988 }
989
990 pub fn currency(&self) -> &CurrencyCode {
991 &self.currency
992 }
993
994 pub fn previous_invoice_hash(&self) -> &str {
995 &self.previous_invoice_hash
996 }
997
998 pub fn invoice_counter(&self) -> u64 {
999 self.invoice_counter
1000 }
1001
1002 pub fn note(&self) -> Option<&InvoiceNote> {
1003 self.note.as_ref()
1004 }
1005
1006 pub fn seller(&self) -> &Seller {
1007 &self.seller
1008 }
1009
1010 pub fn buyer(&self) -> Option<&Buyer> {
1011 self.buyer.as_ref()
1012 }
1013
1014 pub fn line_items(&self) -> &[LineItem] {
1015 &self.line_items
1016 }
1017
1018 pub fn payment_means_code(&self) -> &str {
1019 &self.payment_means_code
1020 }
1021
1022 pub fn vat_category(&self) -> VatCategory {
1023 self.vat_category
1024 }
1025
1026 pub fn flags(&self) -> InvoiceFlags {
1027 self.flags
1028 }
1029
1030 pub fn is_third_party(&self) -> bool {
1031 self.flags.contains(InvoiceFlags::THIRD_PARTY)
1032 }
1033
1034 pub fn is_nominal(&self) -> bool {
1035 self.flags.contains(InvoiceFlags::NOMINAL)
1036 }
1037
1038 pub fn is_export(&self) -> bool {
1039 self.flags.contains(InvoiceFlags::EXPORT)
1040 }
1041
1042 pub fn is_summary(&self) -> bool {
1043 self.flags.contains(InvoiceFlags::SUMMARY)
1044 }
1045
1046 pub fn is_self_billed(&self) -> bool {
1047 self.flags.contains(InvoiceFlags::SELF_BILLED)
1048 }
1049
1050 pub fn invoice_level_charge(&self) -> f64 {
1051 self.invoice_level_charge
1052 }
1053
1054 pub fn invoice_level_discount(&self) -> f64 {
1055 self.invoice_level_discount
1056 }
1057
1058 pub fn allowance_reason(&self) -> Option<&str> {
1059 self.allowance_reason.as_deref()
1060 }
1061
1062 pub(crate) fn seller_name(&self) -> QrResult<&str> {
1063 let name = self.seller.name.trim();
1064 if name.is_empty() {
1065 return Err(QrCodeError::MissingSellerName);
1066 }
1067 Ok(name)
1068 }
1069
1070 pub(crate) fn seller_vat(&self) -> QrResult<&str> {
1071 let vat = self
1072 .seller
1073 .vat_id
1074 .as_ref()
1075 .ok_or(QrCodeError::MissingSellerVat)?
1076 .as_str()
1077 .trim();
1078 if vat.is_empty() {
1079 return Err(QrCodeError::MissingSellerVat);
1080 }
1081 Ok(vat)
1082 }
1083
1084 pub(crate) fn issue_date_string(&self) -> String {
1085 self.issue_datetime.date_str().to_string()
1086 }
1087
1088 pub(crate) fn issue_time_string(&self) -> String {
1089 self.issue_datetime.time_str().to_string()
1090 }
1091
1092 pub(crate) fn format_amount(amount: f64) -> String {
1093 format!("{:.2}", amount)
1094 }
1095}
1096#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
1106pub struct InvoiceTotalsData {
1107 line_extension: f64,
1108 tax_amount: f64,
1109 allowance_total: f64,
1110 charge_total: f64,
1111}
1112
1113impl InvoiceTotalsData {
1114 pub(crate) fn from_data(data: &InvoiceData) -> Self {
1115 let line_extension: f64 = data.line_items.iter().map(|li| li.total_amount).sum();
1116 let tax_amount: f64 = data.line_items.iter().map(|li| li.vat_amount).sum();
1117
1118 Self {
1119 line_extension,
1120 tax_amount,
1121 allowance_total: data.invoice_level_discount,
1122 charge_total: data.invoice_level_charge,
1123 }
1124 }
1125
1126 pub fn line_extension(&self) -> f64 {
1127 self.line_extension
1128 }
1129
1130 pub fn tax_amount(&self) -> f64 {
1131 self.tax_amount
1132 }
1133
1134 pub fn allowance_total(&self) -> f64 {
1135 self.allowance_total
1136 }
1137
1138 pub fn charge_total(&self) -> f64 {
1139 self.charge_total
1140 }
1141
1142 pub fn taxable_amount(&self) -> f64 {
1143 self.line_extension - self.allowance_total + self.charge_total
1144 }
1145
1146 pub fn tax_inclusive_amount(&self) -> f64 {
1147 self.taxable_amount() + self.tax_amount
1148 }
1149}