generateZATCAXml static method

XmlDocument generateZATCAXml(
  1. BaseInvoice invoice,
  2. Supplier supplier
)

Generate a ZATCA-compliant XML string for the invoice data.

Implementation

static XmlDocument generateZATCAXml(BaseInvoice invoice, Supplier supplier) {
  final builder = XmlBuilder();
  final formatter = NumberFormat("#.##");
  builder.processing('xml', 'version="1.0" encoding="UTF-8"');
  builder.element(
    'Invoice',
    nest: () {
      builder.attribute(
        'xmlns',
        'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2',
      );
      builder.attribute(
        'xmlns:cac',
        'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2',
      );
      builder.attribute(
        'xmlns:cbc',
        'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2',
      );
      builder.attribute(
        'xmlns:ext',
        'urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2',
      );
      builder.element('cbc:ProfileID', nest: invoice.profileID);
      builder.element('cbc:ID', nest: invoice.invoiceNumber);
      builder.element('cbc:UUID', nest: invoice.uuid);
      builder.element('cbc:IssueDate', nest: invoice.issueDate);
      builder.element('cbc:IssueTime', nest: invoice.issueTime);

      builder.element(
        'cbc:InvoiceTypeCode',
        nest: () {
          builder.attribute(
            'name',
            invoice.invoiceType.invoiceRelationType.value,
          );
          builder.text(invoice.invoiceType.code);
        },
      );

      // builder.element(
      //   'cbc:Note',
      //   nest: () {
      //     builder.attribute('languageID', 'ar');
      //     builder.text(data.note);
      //   },
      // );
      builder.element('cbc:DocumentCurrencyCode', nest: invoice.currencyCode);
      builder.element('cbc:TaxCurrencyCode', nest: invoice.taxCurrencyCode);

      if (invoice is DBInvoice) {
        builder.element(
          'cac:BillingReference',
          nest: () {
            builder.element(
              'cac:InvoiceDocumentReference',
              nest: () {
                builder.element(
                  'cbc:ID',
                  nest: () {
                    builder.text(
                      invoice.cancellation.canceledSerialInvoiceNumber,
                    );
                  },
                );
              },
            );
          },
        );
      }

      builder.element(
        'cac:AdditionalDocumentReference',
        nest: () {
          builder.element('cbc:ID', nest: 'ICV');
          builder.element('cbc:UUID', nest: '1');
        },
      );
      builder.element(
        'cac:AdditionalDocumentReference',
        nest: () {
          builder.element('cbc:ID', nest: 'PIH');
          builder.element(
            'cac:Attachment',
            nest: () {
              builder.element(
                'cbc:EmbeddedDocumentBinaryObject',
                nest: invoice.previousInvoiceHash,
                attributes: {'mimeCode': 'text/plain'},
              );
            },
          );
        },
      );

      // Supplier
      builder.element(
        'cac:AccountingSupplierParty',
        nest: () {
          builder.element(
            'cac:Party',
            nest: () {
              builder.element(
                'cac:PartyIdentification',
                nest: () {
                  builder.element(
                    'cbc:ID',
                    nest: () {
                      builder.attribute('schemeID', 'CRN');
                      builder.text(supplier.companyCRN);
                    },
                  );
                },
              );
              builder.element(
                'cac:PostalAddress',
                nest: () {
                  builder.element(
                    'cbc:StreetName',
                    nest: supplier.location.street,
                  );
                  builder.element(
                    'cbc:BuildingNumber',
                    nest: supplier.location.building,
                  );
                  builder.element(
                    'cbc:PlotIdentification',
                    nest: supplier.location.plotIdentification,
                  );
                  builder.element(
                    'cbc:CitySubdivisionName',
                    nest: supplier.location.citySubdivision,
                  );
                  builder.element(
                    'cbc:CityName',
                    nest: supplier.location.city,
                  );
                  builder.element(
                    'cbc:PostalZone',
                    nest: supplier.location.postalZone,
                  );
                  builder.element(
                    'cac:Country',
                    nest: () {
                      builder.element(
                        'cbc:IdentificationCode',
                        nest: supplier.location.countryCode,
                      );
                    },
                  );
                },
              );
              builder.element(
                'cac:PartyTaxScheme',
                nest: () {
                  builder.element('cbc:CompanyID', nest: supplier.companyID);
                  builder.element(
                    'cac:TaxScheme',
                    nest: () {
                      builder.element('cbc:ID', nest: 'VAT');
                    },
                  );
                },
              );
              builder.element(
                'cac:PartyLegalEntity',
                nest: () {
                  builder.element(
                    'cbc:RegistrationName',
                    nest: supplier.registrationName,
                  );
                },
              );
            },
          );
        },
      );

      // Customer
      builder.element(
        'cac:AccountingCustomerParty',
        nest: () {
          builder.element(
            'cac:Party',
            nest: () {
              builder.element(
                'cac:PartyIdentification',
                nest: () {
                  builder.element(
                    'cbc:ID',
                    nest: () {
                      builder.attribute('schemeID', 'CRN');
                      builder.text('');
                    },
                  );
                },
              );
              builder.element(
                'cac:PostalAddress',
                nest: () {
                  builder.element(
                    'cbc:StreetName',
                    nest: invoice.customer!.address.street,
                  );
                  builder.element(
                    'cbc:BuildingNumber',
                    nest: invoice.customer!.address.building,
                  );
                  builder.element(
                    'cbc:CitySubdivisionName',
                    nest: invoice.customer!.address.citySubdivision,
                  );
                  builder.element(
                    'cbc:CityName',
                    nest: invoice.customer!.address.city,
                  );
                  builder.element(
                    'cbc:PostalZone',
                    nest: invoice.customer!.address.postalZone,
                  );
                  builder.element(
                    'cac:Country',
                    nest: () {
                      builder.element(
                        'cbc:IdentificationCode',
                        nest: invoice.customer!.address.countryCode,
                      );
                    },
                  );
                },
              );
              builder.element(
                'cac:PartyTaxScheme',
                nest: () {
                  builder.element(
                    'cbc:CompanyID',
                    nest: invoice.customer!.companyID,
                  );
                  builder.element(
                    'cac:TaxScheme',
                    nest: () {
                      builder.element('cbc:ID', nest: 'VAT');
                    },
                  );
                },
              );
              builder.element(
                'cac:PartyLegalEntity',
                nest: () {
                  builder.element(
                    'cbc:RegistrationName',
                    nest: invoice.customer!.registrationName,
                  );
                },
              );
            },
          );
        },
      );

      if (invoice is Invoice) {
        builder.element(
          'cac:Delivery',
          nest: () {
            builder.element(
              'cbc:ActualDeliveryDate',
              nest: invoice.actualDeliveryDate,
            );
          },
        );
      }
      if (invoice is DBInvoice) {
        builder.element(
          'cac:PaymentMeans',
          nest: () {
            builder.element(
              'cbc:PaymentMeansCode',
              nest: invoice.cancellation.paymentMethod.value,
            );
            builder.element(
              'cbc:InstructionNote',
              nest: invoice.cancellation.reason,
            );
          },
        );
      }

      // Totals
      builder.element(
        'cac:TaxTotal',
        nest: () {
          builder.element(
            'cbc:TaxAmount',
            nest: () {
              builder.attribute('currencyID', 'SAR');
              builder.text(invoice.taxAmount.toStringAsFixed(2));
            },
          );
          builder.element(
            'cac:TaxSubtotal',
            nest: () {
              builder.element(
                'cbc:TaxableAmount',
                nest: () {
                  builder.attribute('currencyID', 'SAR');
                  builder.text(
                    formatter.format(invoice.totalAmount - invoice.taxAmount),
                  );
                },
              );
              builder.element(
                'cbc:TaxAmount',
                nest: () {
                  builder.attribute('currencyID', 'SAR');
                  builder.text(formatter.format(invoice.taxAmount));
                },
              );
              builder.element(
                'cac:TaxCategory',
                nest: () {
                  builder.element(
                    'cbc:ID',
                    nest: () {
                      builder.attribute('schemeAgencyID', '6');
                      builder.attribute('schemeID', 'UN/ECE 5305');
                      builder.text('S');
                    },
                  );
                  builder.element('cbc:Percent', nest: '15');
                  builder.element(
                    'cac:TaxScheme',
                    nest: () {
                      builder.element(
                        'cbc:ID',
                        nest: () {
                          builder.attribute('schemeAgencyID', '6');
                          builder.attribute('schemeID', 'UN/ECE 5153');
                          builder.text('VAT');
                        },
                      );
                    },
                  );
                },
              );
            },
          );
        },
      );
      builder.element(
        'cac:TaxTotal',
        nest: () {
          builder.element(
            'cbc:TaxAmount',
            nest: () {
              builder.attribute('currencyID', 'SAR');
              builder.text(invoice.taxAmount.toStringAsFixed(2));
            },
          );
        },
      );

      builder.element(
        'cac:LegalMonetaryTotal',
        nest: () {
          double taxableAmount = invoice.totalAmount - invoice.taxAmount;
          builder.element(
            'cbc:LineExtensionAmount',
            nest: () {
              builder.attribute('currencyID', 'SAR');
              builder.text(taxableAmount.toStringAsFixed(2));
            },
          );
          builder.element(
            'cbc:TaxExclusiveAmount',
            nest: () {
              builder.attribute('currencyID', 'SAR');
              builder.text(formatter.format(taxableAmount));
            },
          );
          builder.element(
            'cbc:TaxInclusiveAmount',
            nest: () {
              builder.attribute('currencyID', 'SAR');
              builder.text(invoice.totalAmount.toStringAsFixed(2));
            },
          );
          builder.element(
            'cbc:PrepaidAmount',
            nest: () {
              builder.attribute('currencyID', 'SAR');
              builder.text('0');
            },
          );
          builder.element(
            'cbc:PayableAmount',
            nest: () {
              builder.attribute('currencyID', 'SAR');
              builder.text(invoice.totalAmount.toStringAsFixed(2));
            },
          );
        },
      );

      // Invoice Lines
      for (var line in invoice.invoiceLines) {
        builder.element(
          'cac:InvoiceLine',
          nest: () {
            builder.element('cbc:ID', nest: line.id);
            builder.element(
              'cbc:InvoicedQuantity',
              nest: () {
                builder.attribute('unitCode', line.unitCode);
                builder.text(line.quantity);
              },
            );
            builder.element(
              'cbc:LineExtensionAmount',
              nest: () {
                builder.attribute('currencyID', 'SAR');
                builder.text(line.lineExtensionAmount.toStringAsFixed(2));
              },
            );
            builder.element(
              'cac:TaxTotal',
              nest: () {
                builder.element(
                  'cbc:TaxAmount',
                  nest: () {
                    builder.attribute('currencyID', 'SAR');
                    builder.text(line.taxAmount.toStringAsFixed(2));
                  },
                );
                double roundingAmount = line.roundingAmount;
                builder.element(
                  'cbc:RoundingAmount',
                  nest: () {
                    builder.attribute('currencyID', 'SAR');
                    builder.text(roundingAmount.toStringAsFixed(2));
                  },
                );
              },
            );
            builder.element(
              'cac:Item',
              nest: () {
                builder.element('cbc:Name', nest: line.itemName);
                builder.element(
                  'cac:ClassifiedTaxCategory',
                  nest: () {
                    builder.element('cbc:ID', nest: 'S');
                    builder.element(
                      'cbc:Percent',
                      nest: formatter.format(line.taxPercent),
                    );
                    builder.element(
                      'cac:TaxScheme',
                      nest: () {
                        builder.element('cbc:ID', nest: 'VAT');
                      },
                    );
                  },
                );
              },
            );
            builder.element(
              'cac:Price',
              nest: () {
                builder.element(
                  'cbc:PriceAmount',
                  nest: () {
                    builder.attribute('currencyID', 'SAR');
                    builder.text(
                      line.taxExclusiveDiscountAppliedPrice.toStringAsFixed(
                        14,
                      ),
                    );
                  },
                );
                if (line.discounts.isNotEmpty) {
                  builder.element(
                    'cac:AllowanceCharge',
                    nest: () {
                      for (var discount in line.discounts) {
                        builder.element('cbc:ChargeIndicator', nest: 'false');
                        builder.element(
                          'cbc:AllowanceChargeReason',
                          nest: discount.reason,
                        );
                        builder.element(
                          'cbc:Amount',
                          nest: () {
                            builder.attribute('currencyID', 'SAR');
                            builder.text(discount.amount.toStringAsFixed(14));
                          },
                        );

                        /// Required when the discount is a percentage.
                        // builder.element(
                        //   'cbc:BaseAmount',
                        //   nest: () {
                        //     builder.attribute('currencyID', 'SAR');
                        //     builder.text(line.taxExclusivePrice.toString());
                        //   },
                        // );
                      }
                    },
                  );
                }
              },
            );
          },
        );
      }
    },
  );

  /// Build the XML document
  final document = builder.buildDocument();

  return document;
}