diff --git a/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/mapper/MustangMapper.kt b/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/mapper/MustangMapper.kt index c98c9a5..f65c144 100644 --- a/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/mapper/MustangMapper.kt +++ b/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/mapper/MustangMapper.kt @@ -2,6 +2,7 @@ package net.codinux.invoicing.mapper import net.codinux.invoicing.model.LineItem import net.codinux.invoicing.model.Party +import org.mustangproject.Contact import org.mustangproject.Invoice import org.mustangproject.Item import org.mustangproject.Product @@ -27,14 +28,16 @@ class MustangMapper { } fun mapParty(party: Party): TradeParty = TradeParty( - party.name, party.street, party.postalCode, party.city, party.country + party.name, party.street, party.postalCode, party.city, party.countryIsoCode ).apply { - this.taxID = party.taxNumber - // TODO: vatID? + this.taxID = party.vatId // TODO: ID? // TODO: description? this.email = party.email + this.setContact(Contact(party.contactName, party.phone, party.email).apply { + this.fax = party.fax + }) } fun mapLineItem(item: LineItem): IZUGFeRDExportableItem = Item( diff --git a/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/model/Party.kt b/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/model/Party.kt index 33ed8ea..856065f 100644 --- a/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/model/Party.kt +++ b/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/model/Party.kt @@ -9,13 +9,18 @@ class Party( val street: String, var postalCode: String?, val city: String, - val country: String? = null, + /** + * Two letter country ISO code, e.g. "us" for USA, "fr" for France, ... + */ + val countryIsoCode: String? = null, // TODO: use the full country name here and map to ISO code in MustangMapper? - val taxNumber: String? = null, // better name like vatTaxNumber? + val vatId: String? = null, + // actually there can be multiple contacts in eInvoice data model, all containing an email, phone, fax and contact name val email: String? = null, -// var telephoneNumber: String? = null, // simply telephone? -// var website: String? = null, + var phone: String? = null, + var fax: String? = null, + var contactName: String? = null, ) { override fun toString() = "$name, $city" } \ No newline at end of file diff --git a/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/creation/EInvoiceCreatorTest.kt b/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/creation/EInvoiceCreatorTest.kt index 6edd27d..0c2d921 100644 --- a/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/creation/EInvoiceCreatorTest.kt +++ b/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/creation/EInvoiceCreatorTest.kt @@ -42,16 +42,16 @@ class EInvoiceCreatorTest { asserter.xpathHasValue("//rsm:ExchangedDocument/ram:IssueDateTime/udt:DateTimeString", DataGenerator.InvoicingDate.toString().replace("-", "")) val senderXPath = "//rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty" - assertParty(asserter, senderXPath, DataGenerator.SenderName, DataGenerator.SenderStreet, DataGenerator.SenderPostalCode, DataGenerator.SenderCity, DataGenerator.SenderVatId, DataGenerator.SenderEmail) + assertParty(asserter, senderXPath, DataGenerator.SenderName, DataGenerator.SenderStreet, DataGenerator.SenderPostalCode, DataGenerator.SenderCity, DataGenerator.SenderVatId, DataGenerator.SenderEmail, DataGenerator.SenderPhone) val receiverXPath = "//rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeAgreement/ram:BuyerTradeParty" - assertParty(asserter, receiverXPath, DataGenerator.RecipientName, DataGenerator.RecipientStreet, DataGenerator.RecipientPostalCode, DataGenerator.RecipientCity, DataGenerator.RecipientVatId, DataGenerator.RecipientEmail) + assertParty(asserter, receiverXPath, DataGenerator.RecipientName, DataGenerator.RecipientStreet, DataGenerator.RecipientPostalCode, DataGenerator.RecipientCity, DataGenerator.RecipientVatId, DataGenerator.RecipientEmail, DataGenerator.RecipientPhone) val lineItemXPath = "//rsm:SupplyChainTradeTransaction/ram:IncludedSupplyChainTradeLineItem" assertLineItem(asserter, lineItemXPath, DataGenerator.ItemName, DataGenerator.ItemUnit, DataGenerator.ItemQuantity, DataGenerator.ItemPrice, DataGenerator.ItemVat, DataGenerator.ItemDescription) } - private fun assertParty(asserter: XPathAsserter, partyXPath: String, name: String, street: String, postalCode: String, city: String, vatId: String, email: String) { + private fun assertParty(asserter: XPathAsserter, partyXPath: String, name: String, street: String, postalCode: String, city: String, vatId: String, email: String, phone: String) { asserter.xpathHasValue("$partyXPath/ram:Name", name) asserter.xpathHasValue("$partyXPath/ram:PostalTradeAddress/ram:LineOne", street) @@ -61,6 +61,8 @@ class EInvoiceCreatorTest { asserter.xpathHasValue("$partyXPath/ram:SpecifiedTaxRegistration/ram:ID", vatId) asserter.xpathHasValue("$partyXPath/ram:URIUniversalCommunication/ram:URIID", email) + asserter.xpathHasValue("$partyXPath/ram:DefinedTradeContact/ram:EmailURIUniversalCommunication/ram:URIID", email) + asserter.xpathHasValue("$partyXPath/ram:DefinedTradeContact/ram:TelephoneUniversalCommunication/ram:CompleteNumber", phone) } private fun assertLineItem(asserter: XPathAsserter, partyXPath: String, name: String, unit: String, quantity: BigDecimal, price: BigDecimal, vatPercentage: BigDecimal, description: String?) { diff --git a/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/test/DataGenerator.kt b/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/test/DataGenerator.kt index 7870dba..d564562 100644 --- a/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/test/DataGenerator.kt +++ b/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/test/DataGenerator.kt @@ -15,17 +15,19 @@ object DataGenerator { const val SenderStreet = "Fun Street 1" const val SenderPostalCode = "12345" const val SenderCity = "Glückstadt" - val SenderCountry: String? = null + const val SenderCountry = "DE" const val SenderVatId = "DE12345678" const val SenderEmail = "working-class-hero@rock.me" + const val SenderPhone = "+4917012345678" const val RecipientName = "Untertänigster Leistungsempfänger" const val RecipientStreet = "Party Street 1" const val RecipientPostalCode = SenderPostalCode const val RecipientCity = SenderCity - val RecipientCountry: String? = SenderCountry + const val RecipientCountry = "DE" const val RecipientVatId = "DE87654321" const val RecipientEmail = "exploiter@your.boss" + const val RecipientPhone = "+4912345678" const val ItemName = "Erbrachte Dienstleistungen" const val ItemUnit = "HUR" // EN code for 'hour' @@ -38,8 +40,8 @@ object DataGenerator { fun createInvoice( invoiceNumber: String = InvoiceNumber, invoicingDate: LocalDate = InvoicingDate, - sender: Party = createParty(SenderName, SenderStreet, SenderPostalCode, SenderCity, SenderCountry, SenderVatId, SenderEmail), - recipient: Party = createParty(RecipientName, RecipientStreet, RecipientPostalCode, RecipientCity, RecipientCountry, RecipientVatId, RecipientEmail), + sender: Party = createParty(SenderName, SenderStreet, SenderPostalCode, SenderCity, SenderCountry, SenderVatId, SenderEmail, SenderPhone), + recipient: Party = createParty(RecipientName, RecipientStreet, RecipientPostalCode, RecipientCity, RecipientCountry, RecipientVatId, RecipientEmail, RecipientPhone), items: List = listOf(createItem()), dueDate: LocalDate? = null ) = Invoice(invoiceNumber, invoicingDate, sender, recipient, items, dueDate) @@ -50,9 +52,12 @@ object DataGenerator { postalCode: String = SenderPostalCode, city: String = SenderCity, country: String? = SenderCountry, - taxNumber: String? = SenderVatId, + vatId: String? = SenderVatId, email: String? = SenderEmail, - ) = Party(name, streetName, postalCode, city, country, taxNumber, email) + phone: String? = SenderPhone, + fax: String? = null, + contactName: String? = null + ) = Party(name, streetName, postalCode, city, country, vatId, email, phone, fax, contactName) fun createItem( name: String = ItemName,