|
7 | 7 |
|
8 | 8 | from ksef.models.invoice import ( |
9 | 9 | Address, |
| 10 | + EuVatIdentification, |
| 11 | + ForeignIdentification, |
10 | 12 | Invoice, |
11 | 13 | InvoiceData, |
12 | 14 | InvoiceType, |
13 | 15 | Issuer, |
14 | 16 | IssuerIdentificationData, |
| 17 | + NipIdentification, |
| 18 | + NoIdentification, |
15 | 19 | Subject, |
16 | 20 | SubjectIdentificationData, |
17 | 21 | ) |
@@ -156,3 +160,115 @@ def test_without_apartment_number() -> None: |
156 | 160 | actual_content = convert_invoice_to_xml(invoice) |
157 | 161 | assert_xml_equal(actual_content=actual_content, expected_content=expected_content) |
158 | 162 | assert b"NrLokalu" not in actual_content |
| 163 | + |
| 164 | + |
| 165 | +def _make_invoice(recipient: Subject) -> Invoice: |
| 166 | + """Build a minimal invoice with the given recipient.""" |
| 167 | + return Invoice( |
| 168 | + issuer=Issuer( |
| 169 | + identification_data=IssuerIdentificationData( |
| 170 | + nip="1111111111", full_name="Example Company 1 Sp z o. o." |
| 171 | + ), |
| 172 | + email="example@example.com", |
| 173 | + phone="+48 111111111", |
| 174 | + address=Address( |
| 175 | + country_code="PL", |
| 176 | + city="Warszawa", |
| 177 | + street="Kwiatowa", |
| 178 | + house_number="1", |
| 179 | + apartment_number="2", |
| 180 | + postal_code="00-001", |
| 181 | + ), |
| 182 | + ), |
| 183 | + recipient=recipient, |
| 184 | + invoice_data=InvoiceData( |
| 185 | + currency_code="PLN", |
| 186 | + issue_date=date(2024, 1, 22), |
| 187 | + issue_number="FA/1/2024", |
| 188 | + sell_date=date(2024, 1, 1), |
| 189 | + total_amount=Decimal("450.00"), |
| 190 | + invoice_annotations=InvoiceAnnotations( |
| 191 | + tax_settlement_on_payment=TaxSettlementOnPayment.REGULAR, |
| 192 | + self_invoice=SelfInvoicing.NO, |
| 193 | + reverse_charge=ReverseCharge.NO, |
| 194 | + split_payment=SplitPayment.NO, |
| 195 | + free_from_vat=FreeFromVat.NO, |
| 196 | + intra_community_supply_of_new_transport_methods=IntraCommunitySupplyOfNewTransportMethods.NO, |
| 197 | + simplified_procedure_by_second_tax_payer=SimplifiedProcedureBySecondTaxPayer.NO, |
| 198 | + margin_procedure=MarginProcedure.NO, |
| 199 | + ), |
| 200 | + invoice_type=InvoiceType.REGULAR_VAT, |
| 201 | + invoice_rows=InvoiceRows( |
| 202 | + rows=[ |
| 203 | + InvoiceRow(name="Example service 1", tax=23), |
| 204 | + InvoiceRow(name="Example service 2", tax=8), |
| 205 | + ] |
| 206 | + ), |
| 207 | + ), |
| 208 | + creation_datetime=datetime(2024, 1, 22, 10, 30, 0, tzinfo=timezone.utc), |
| 209 | + ) |
| 210 | + |
| 211 | + |
| 212 | +def test_recipient_eu_vat() -> None: |
| 213 | + """Test EU company recipient produces KodUE + NrVatUE elements.""" |
| 214 | + invoice = _make_invoice( |
| 215 | + Subject( |
| 216 | + identification_data=EuVatIdentification(eu_country_code="DE", eu_vat_number="123456789") |
| 217 | + ) |
| 218 | + ) |
| 219 | + xml = convert_invoice_to_xml(invoice) |
| 220 | + soup = BeautifulSoup(xml, "xml") |
| 221 | + id_data = soup.find("Podmiot2").find("DaneIdentyfikacyjne") |
| 222 | + assert id_data.find("KodUE").text == "DE" |
| 223 | + assert id_data.find("NrVatUE").text == "123456789" |
| 224 | + assert id_data.find("NIP") is None |
| 225 | + |
| 226 | + |
| 227 | +def test_recipient_foreign_id() -> None: |
| 228 | + """Test non-EU recipient with country code produces KodKraju + NrID.""" |
| 229 | + invoice = _make_invoice( |
| 230 | + Subject(identification_data=ForeignIdentification(country_code="US", tax_id="EIN123")) |
| 231 | + ) |
| 232 | + xml = convert_invoice_to_xml(invoice) |
| 233 | + soup = BeautifulSoup(xml, "xml") |
| 234 | + id_data = soup.find("Podmiot2").find("DaneIdentyfikacyjne") |
| 235 | + assert id_data.find("KodKraju").text == "US" |
| 236 | + assert id_data.find("NrID").text == "EIN123" |
| 237 | + assert id_data.find("NIP") is None |
| 238 | + |
| 239 | + |
| 240 | +def test_recipient_foreign_id_without_country() -> None: |
| 241 | + """Test non-EU recipient without country code produces only NrID.""" |
| 242 | + invoice = _make_invoice(Subject(identification_data=ForeignIdentification(tax_id="XYZ999"))) |
| 243 | + xml = convert_invoice_to_xml(invoice) |
| 244 | + soup = BeautifulSoup(xml, "xml") |
| 245 | + id_data = soup.find("Podmiot2").find("DaneIdentyfikacyjne") |
| 246 | + assert id_data.find("NrID").text == "XYZ999" |
| 247 | + assert id_data.find("KodKraju") is None |
| 248 | + |
| 249 | + |
| 250 | +def test_recipient_no_id() -> None: |
| 251 | + """Test individual/B2C recipient produces BrakID and supports Nazwa.""" |
| 252 | + invoice = _make_invoice(Subject(identification_data=NoIdentification(), name="Jan Kowalski")) |
| 253 | + xml = convert_invoice_to_xml(invoice) |
| 254 | + soup = BeautifulSoup(xml, "xml") |
| 255 | + podmiot2 = soup.find("Podmiot2") |
| 256 | + id_data = podmiot2.find("DaneIdentyfikacyjne") |
| 257 | + assert id_data.find("BrakID").text == "1" |
| 258 | + assert id_data.find("NIP") is None |
| 259 | + assert podmiot2.find("Nazwa").text == "Jan Kowalski" |
| 260 | + |
| 261 | + |
| 262 | +def test_recipient_with_name() -> None: |
| 263 | + """Test NIP recipient with name produces both NIP and Nazwa.""" |
| 264 | + invoice = _make_invoice( |
| 265 | + Subject( |
| 266 | + identification_data=NipIdentification(nip="2222222222"), |
| 267 | + name="Firma Testowa Sp. z o.o.", |
| 268 | + ) |
| 269 | + ) |
| 270 | + xml = convert_invoice_to_xml(invoice) |
| 271 | + soup = BeautifulSoup(xml, "xml") |
| 272 | + podmiot2 = soup.find("Podmiot2") |
| 273 | + assert podmiot2.find("DaneIdentyfikacyjne").find("NIP").text == "2222222222" |
| 274 | + assert podmiot2.find("Nazwa").text == "Firma Testowa Sp. z o.o." |
0 commit comments