validateInvoiceDataModel function
Validates an InvoiceDataModel before encoding.
Implementation
void validateInvoiceDataModel(InvoiceDataModel model) {
_requireNonEmpty(model.invoiceId, 'invoiceId');
_requireNonEmpty(model.issueDate, 'issueDate');
_requireDate(model.issueDate, 'issueDate');
_requireDate(model.taxPointDate, 'taxPointDate');
_requireNonEmpty(model.localCurrencyCode, 'localCurrencyCode');
if (!_isValidCurrencyCode(model.localCurrencyCode)) {
throw const BysquareValidationError(
'Invalid currency code. Must be 3 uppercase letters (ISO 4217).',
'localCurrencyCode',
);
}
// Foreign currency group: all three or none
final hasForeign = model.foreignCurrencyCode != null;
final hasCurrRate = model.currRate != null;
final hasRefRate = model.referenceCurrRate != null;
if (hasForeign != hasCurrRate || hasForeign != hasRefRate) {
throw const BysquareValidationError(
'When any of foreignCurrencyCode, currRate, or referenceCurrRate is '
'set, all three are required.',
'foreignCurrencyCode',
);
}
if (hasForeign && !_isValidCurrencyCode(model.foreignCurrencyCode!)) {
throw const BysquareValidationError(
'Invalid currency code. Must be 3 uppercase letters (ISO 4217).',
'foreignCurrencyCode',
);
}
// Supplier party
_requireNonEmpty(model.supplierParty.partyName, 'supplierParty.partyName');
_requireNonEmpty(
model.supplierParty.postalAddress.streetName,
'supplierParty.postalAddress.streetName',
);
_requireNonEmpty(
model.supplierParty.postalAddress.cityName,
'supplierParty.postalAddress.cityName',
);
_requireNonEmpty(
model.supplierParty.postalAddress.postalZone,
'supplierParty.postalAddress.postalZone',
);
_requireNonEmpty(
model.supplierParty.postalAddress.country,
'supplierParty.postalAddress.country',
);
if (model.supplierParty.postalAddress.country != null &&
!_isValidCountryCode(model.supplierParty.postalAddress.country!)) {
throw const BysquareValidationError(
'Invalid country code. Must be 2-3 uppercase letters.',
'supplierParty.postalAddress.country',
);
}
// Customer party
_requireNonEmpty(model.customerParty.partyName, 'customerParty.partyName');
// Invoice line: exactly one of numberOfInvoiceLines or singleInvoiceLine
final hasLineCount = model.numberOfInvoiceLines != null;
final hasSingleLine = model.singleInvoiceLine != null;
if (hasLineCount == hasSingleLine) {
throw const BysquareValidationError(
'Exactly one of numberOfInvoiceLines or singleInvoiceLine must be set.',
'numberOfInvoiceLines',
);
}
if (hasLineCount && model.numberOfInvoiceLines! <= 0) {
throw const BysquareValidationError(
'numberOfInvoiceLines must be a positive integer.',
'numberOfInvoiceLines',
);
}
// Single invoice line validation
if (model.singleInvoiceLine != null) {
final line = model.singleInvoiceLine!;
final hasName = line.itemName != null;
final hasEan = line.itemEanCode != null;
if (hasName == hasEan) {
throw const BysquareValidationError(
'Exactly one of itemName or itemEanCode must be set.',
'singleInvoiceLine.itemName',
);
}
final hasFrom = line.periodFromDate != null;
final hasTo = line.periodToDate != null;
if (hasFrom != hasTo) {
throw const BysquareValidationError(
'Both periodFromDate and periodToDate must be set together.',
'singleInvoiceLine.periodFromDate',
);
}
if (hasFrom) {
_requireDate(line.periodFromDate, 'singleInvoiceLine.periodFromDate');
_requireDate(line.periodToDate, 'singleInvoiceLine.periodToDate');
if (line.periodFromDate!.compareTo(line.periodToDate!) > 0) {
throw const BysquareValidationError(
'periodFromDate must not be after periodToDate.',
'singleInvoiceLine.periodFromDate',
);
}
}
}
// Tax category summaries
if (model.taxCategorySummaries.isEmpty) {
throw const BysquareValidationError(
'At least one tax category summary is required.',
'taxCategorySummaries',
);
}
for (var i = 0; i < model.taxCategorySummaries.length; i++) {
final tcs = model.taxCategorySummaries[i];
if (tcs.classifiedTaxCategory < 0 || tcs.classifiedTaxCategory > 1) {
throw BysquareValidationError(
'classifiedTaxCategory must be a number in range [0, 1].',
'taxCategorySummaries[$i].classifiedTaxCategory',
);
}
}
}