# coding: utf-8

"""
    FactPulse REST API

     REST API for electronic invoicing in France: Factur-X, AFNOR PDP/PA, electronic signatures.  ## 🎯 Main Features  ### 📄 Factur-X Invoice Generation - **Formats**: XML only or PDF/A-3 with embedded XML - **Profiles**: MINIMUM, BASIC, EN16931, EXTENDED - **Standards**: EN 16931 (EU directive 2014/55), ISO 19005-3 (PDF/A-3), CII (UN/CEFACT) - **🆕 Simplified Format**: Generation from SIRET + auto-enrichment (Chorus Pro API + Business Search)  ### ✅ Validation and Compliance - **XML Validation**: Schematron (45 to 210+ rules depending on profile) - **PDF Validation**: PDF/A-3, Factur-X XMP metadata, electronic signatures - **VeraPDF**: Strict PDF/A validation (146+ ISO 19005-3 rules) - **Asynchronous Processing**: Celery support for heavy validations (VeraPDF)  ### 📡 AFNOR PDP/PA Integration (XP Z12-013) - **Flow Submission**: Send invoices to Partner Dematerialization Platforms - **Flow Search**: View submitted invoices - **Download**: Retrieve PDF/A-3 with XML - **Directory Service**: Company search (SIREN/SIRET) - **Multi-client**: Support for multiple PDP configs per user (stored credentials or zero-storage)  ### ✍️ PDF Electronic Signature - **Standards**: PAdES-B-B, PAdES-B-T (RFC 3161 timestamping), PAdES-B-LT (long-term archival) - **eIDAS Levels**: SES (self-signed), AdES (commercial CA), QES (QTSP) - **Validation**: Cryptographic integrity and certificate verification - **Certificate Generation**: Self-signed X.509 certificates for testing  ### 🔄 Asynchronous Processing - **Celery**: Asynchronous generation, validation and signing - **Polling**: Status tracking via `/tasks/{task_id}/status` - **No timeout**: Ideal for large files or heavy validations  ## 🔒 Authentication  All requests require a **JWT token** in the Authorization header: ``` Authorization: Bearer YOUR_JWT_TOKEN ```  ### How to obtain a JWT token?  #### 🔑 Method 1: `/api/token/` API (Recommended)  **URL:** `https://factpulse.fr/api/token/`  This method is **recommended** for integration in your applications and CI/CD workflows.  **Prerequisites:** Having set a password on your account  **For users registered via email/password:** - You already have a password, use it directly  **For users registered via OAuth (Google/GitHub):** - You must first set a password at: https://factpulse.fr/accounts/password/set/ - Once the password is created, you can use the API  **Request example:** ```bash curl -X POST https://factpulse.fr/api/token/ \\   -H \"Content-Type: application/json\" \\   -d '{     \"username\": \"your_email@example.com\",     \"password\": \"your_password\"   }' ```  **Optional `client_uid` parameter:**  To select credentials for a specific client (PA/PDP, Chorus Pro, signing certificates), add `client_uid`:  ```bash curl -X POST https://factpulse.fr/api/token/ \\   -H \"Content-Type: application/json\" \\   -d '{     \"username\": \"your_email@example.com\",     \"password\": \"your_password\",     \"client_uid\": \"550e8400-e29b-41d4-a716-446655440000\"   }' ```  The `client_uid` will be included in the JWT and allow the API to automatically use: - AFNOR/PDP credentials configured for this client - Chorus Pro credentials configured for this client - Electronic signature certificates configured for this client  **Response:** ```json {   \"access\": \"eyJ0eXAiOiJKV1QiLCJhbGc...\",  // Access token (validity: 30 min)   \"refresh\": \"eyJ0eXAiOiJKV1QiLCJhbGc...\"  // Refresh token (validity: 7 days) } ```  **Advantages:** - ✅ Full automation (CI/CD, scripts) - ✅ Programmatic token management - ✅ Refresh token support for automatic access renewal - ✅ Easy integration in any language/tool  #### 🖥️ Method 2: Dashboard Generation (Alternative)  **URL:** https://factpulse.fr/api/dashboard/  This method is suitable for quick tests or occasional use via the graphical interface.  **How it works:** - Log in to the dashboard - Use the \"Generate Test Token\" or \"Generate Production Token\" buttons - Works for **all** users (OAuth and email/password), without requiring a password  **Token types:** - **Test Token**: 24h validity, 1000 calls/day quota (free) - **Production Token**: 7 days validity, quota based on your plan  **Advantages:** - ✅ Quick for API testing - ✅ No password required - ✅ Simple visual interface  **Disadvantages:** - ❌ Requires manual action - ❌ No refresh token - ❌ Less suited for automation  ### 📚 Full Documentation  For more information on authentication and API usage: https://factpulse.fr/documentation-api/     

    The version of the OpenAPI document: 1.0.0
    Contact: contact@factpulse.fr
    Generated by OpenAPI Generator (https://openapi-generator.tech)

    Do not edit the class manually.
"""  # noqa: E501


from __future__ import annotations
import pprint
import re  # noqa: F401
import json

from pydantic import BaseModel, ConfigDict, StrictInt, StrictStr
from typing import Any, ClassVar, Dict, List, Optional
from factpulse.models.additional_document import AdditionalDocument
from factpulse.models.allowance_charge import AllowanceCharge
from factpulse.models.delivery_party import DeliveryParty
from factpulse.models.invoice_line import InvoiceLine
from factpulse.models.invoice_note import InvoiceNote
from factpulse.models.invoice_references import InvoiceReferences
from factpulse.models.invoice_totals import InvoiceTotals
from factpulse.models.invoicing_framework import InvoicingFramework
from factpulse.models.payee import Payee
from factpulse.models.payment_card import PaymentCard
from factpulse.models.recipient import Recipient
from factpulse.models.submission_mode import SubmissionMode
from factpulse.models.supplementary_attachment import SupplementaryAttachment
from factpulse.models.supplier import Supplier
from factpulse.models.tax_representative import TaxRepresentative
from factpulse.models.vat_line import VATLine
from typing import Optional, Set
from typing_extensions import Self

class FacturXInvoice(BaseModel):
    """
    Data model for an invoice to be converted to Factur-X.
    """ # noqa: E501
    invoice_number: StrictStr
    payment_due_date: StrictStr
    invoice_date: Optional[StrictStr] = None
    submission_mode: SubmissionMode
    recipient: Recipient
    supplier: Supplier
    invoicing_framework: InvoicingFramework
    references: InvoiceReferences
    totals: InvoiceTotals
    invoice_lines: Optional[List[InvoiceLine]] = None
    vat_lines: Optional[List[VATLine]] = None
    notes: Optional[List[InvoiceNote]] = None
    comment: Optional[StrictStr] = None
    current_user_id: Optional[StrictInt] = None
    supplementary_attachments: Optional[List[SupplementaryAttachment]] = None
    payee: Optional[Payee] = None
    delivery_party: Optional[DeliveryParty] = None
    tax_representative: Optional[TaxRepresentative] = None
    delivery_date: Optional[StrictStr] = None
    billing_period_start: Optional[StrictStr] = None
    billing_period_end: Optional[StrictStr] = None
    payment_reference: Optional[StrictStr] = None
    creditor_reference_id: Optional[StrictStr] = None
    direct_debit_mandate_id: Optional[StrictStr] = None
    debtor_iban: Optional[StrictStr] = None
    payment_terms: Optional[StrictStr] = None
    allowances_charges: Optional[List[AllowanceCharge]] = None
    additional_documents: Optional[List[AdditionalDocument]] = None
    buyer_accounting_reference: Optional[StrictStr] = None
    payment_card: Optional[PaymentCard] = None
    __properties: ClassVar[List[str]] = ["invoice_number", "payment_due_date", "invoice_date", "submission_mode", "recipient", "supplier", "invoicing_framework", "references", "totals", "invoice_lines", "vat_lines", "notes", "comment", "current_user_id", "supplementary_attachments", "payee", "delivery_party", "tax_representative", "delivery_date", "billing_period_start", "billing_period_end", "payment_reference", "creditor_reference_id", "direct_debit_mandate_id", "debtor_iban", "payment_terms", "allowances_charges", "additional_documents", "buyer_accounting_reference", "payment_card"]

    model_config = ConfigDict(
        populate_by_name=True,
        validate_assignment=True,
        protected_namespaces=(),
    )


    def to_str(self) -> str:
        """Returns the string representation of the model using alias"""
        return pprint.pformat(self.model_dump(by_alias=True))

    def to_json(self) -> str:
        """Returns the JSON representation of the model using alias"""
        # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
        return json.dumps(self.to_dict())

    @classmethod
    def from_json(cls, json_str: str) -> Optional[Self]:
        """Create an instance of FacturXInvoice from a JSON string"""
        return cls.from_dict(json.loads(json_str))

    def to_dict(self) -> Dict[str, Any]:
        """Return the dictionary representation of the model using alias.

        This has the following differences from calling pydantic's
        `self.model_dump(by_alias=True)`:

        * `None` is only added to the output dict for nullable fields that
          were set at model initialization. Other fields with value `None`
          are ignored.
        """
        excluded_fields: Set[str] = set([
        ])

        _dict = self.model_dump(
            by_alias=True,
            exclude=excluded_fields,
            exclude_none=True,
        )
        # override the default output from pydantic by calling `to_dict()` of recipient
        if self.recipient:
            _dict['recipient'] = self.recipient.to_dict()
        # override the default output from pydantic by calling `to_dict()` of supplier
        if self.supplier:
            _dict['supplier'] = self.supplier.to_dict()
        # override the default output from pydantic by calling `to_dict()` of invoicing_framework
        if self.invoicing_framework:
            _dict['invoicing_framework'] = self.invoicing_framework.to_dict()
        # override the default output from pydantic by calling `to_dict()` of references
        if self.references:
            _dict['references'] = self.references.to_dict()
        # override the default output from pydantic by calling `to_dict()` of totals
        if self.totals:
            _dict['totals'] = self.totals.to_dict()
        # override the default output from pydantic by calling `to_dict()` of each item in invoice_lines (list)
        _items = []
        if self.invoice_lines:
            for _item_invoice_lines in self.invoice_lines:
                if _item_invoice_lines:
                    _items.append(_item_invoice_lines.to_dict())
            _dict['invoice_lines'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in vat_lines (list)
        _items = []
        if self.vat_lines:
            for _item_vat_lines in self.vat_lines:
                if _item_vat_lines:
                    _items.append(_item_vat_lines.to_dict())
            _dict['vat_lines'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in notes (list)
        _items = []
        if self.notes:
            for _item_notes in self.notes:
                if _item_notes:
                    _items.append(_item_notes.to_dict())
            _dict['notes'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in supplementary_attachments (list)
        _items = []
        if self.supplementary_attachments:
            for _item_supplementary_attachments in self.supplementary_attachments:
                if _item_supplementary_attachments:
                    _items.append(_item_supplementary_attachments.to_dict())
            _dict['supplementary_attachments'] = _items
        # override the default output from pydantic by calling `to_dict()` of payee
        if self.payee:
            _dict['payee'] = self.payee.to_dict()
        # override the default output from pydantic by calling `to_dict()` of delivery_party
        if self.delivery_party:
            _dict['delivery_party'] = self.delivery_party.to_dict()
        # override the default output from pydantic by calling `to_dict()` of tax_representative
        if self.tax_representative:
            _dict['tax_representative'] = self.tax_representative.to_dict()
        # override the default output from pydantic by calling `to_dict()` of each item in allowances_charges (list)
        _items = []
        if self.allowances_charges:
            for _item_allowances_charges in self.allowances_charges:
                if _item_allowances_charges:
                    _items.append(_item_allowances_charges.to_dict())
            _dict['allowances_charges'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in additional_documents (list)
        _items = []
        if self.additional_documents:
            for _item_additional_documents in self.additional_documents:
                if _item_additional_documents:
                    _items.append(_item_additional_documents.to_dict())
            _dict['additional_documents'] = _items
        # override the default output from pydantic by calling `to_dict()` of payment_card
        if self.payment_card:
            _dict['payment_card'] = self.payment_card.to_dict()
        # set to None if comment (nullable) is None
        # and model_fields_set contains the field
        if self.comment is None and "comment" in self.model_fields_set:
            _dict['comment'] = None

        # set to None if current_user_id (nullable) is None
        # and model_fields_set contains the field
        if self.current_user_id is None and "current_user_id" in self.model_fields_set:
            _dict['current_user_id'] = None

        # set to None if supplementary_attachments (nullable) is None
        # and model_fields_set contains the field
        if self.supplementary_attachments is None and "supplementary_attachments" in self.model_fields_set:
            _dict['supplementary_attachments'] = None

        # set to None if payee (nullable) is None
        # and model_fields_set contains the field
        if self.payee is None and "payee" in self.model_fields_set:
            _dict['payee'] = None

        # set to None if delivery_party (nullable) is None
        # and model_fields_set contains the field
        if self.delivery_party is None and "delivery_party" in self.model_fields_set:
            _dict['delivery_party'] = None

        # set to None if tax_representative (nullable) is None
        # and model_fields_set contains the field
        if self.tax_representative is None and "tax_representative" in self.model_fields_set:
            _dict['tax_representative'] = None

        # set to None if delivery_date (nullable) is None
        # and model_fields_set contains the field
        if self.delivery_date is None and "delivery_date" in self.model_fields_set:
            _dict['delivery_date'] = None

        # set to None if billing_period_start (nullable) is None
        # and model_fields_set contains the field
        if self.billing_period_start is None and "billing_period_start" in self.model_fields_set:
            _dict['billing_period_start'] = None

        # set to None if billing_period_end (nullable) is None
        # and model_fields_set contains the field
        if self.billing_period_end is None and "billing_period_end" in self.model_fields_set:
            _dict['billing_period_end'] = None

        # set to None if payment_reference (nullable) is None
        # and model_fields_set contains the field
        if self.payment_reference is None and "payment_reference" in self.model_fields_set:
            _dict['payment_reference'] = None

        # set to None if creditor_reference_id (nullable) is None
        # and model_fields_set contains the field
        if self.creditor_reference_id is None and "creditor_reference_id" in self.model_fields_set:
            _dict['creditor_reference_id'] = None

        # set to None if direct_debit_mandate_id (nullable) is None
        # and model_fields_set contains the field
        if self.direct_debit_mandate_id is None and "direct_debit_mandate_id" in self.model_fields_set:
            _dict['direct_debit_mandate_id'] = None

        # set to None if debtor_iban (nullable) is None
        # and model_fields_set contains the field
        if self.debtor_iban is None and "debtor_iban" in self.model_fields_set:
            _dict['debtor_iban'] = None

        # set to None if payment_terms (nullable) is None
        # and model_fields_set contains the field
        if self.payment_terms is None and "payment_terms" in self.model_fields_set:
            _dict['payment_terms'] = None

        # set to None if allowances_charges (nullable) is None
        # and model_fields_set contains the field
        if self.allowances_charges is None and "allowances_charges" in self.model_fields_set:
            _dict['allowances_charges'] = None

        # set to None if additional_documents (nullable) is None
        # and model_fields_set contains the field
        if self.additional_documents is None and "additional_documents" in self.model_fields_set:
            _dict['additional_documents'] = None

        # set to None if buyer_accounting_reference (nullable) is None
        # and model_fields_set contains the field
        if self.buyer_accounting_reference is None and "buyer_accounting_reference" in self.model_fields_set:
            _dict['buyer_accounting_reference'] = None

        # set to None if payment_card (nullable) is None
        # and model_fields_set contains the field
        if self.payment_card is None and "payment_card" in self.model_fields_set:
            _dict['payment_card'] = None

        return _dict

    @classmethod
    def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
        """Create an instance of FacturXInvoice from a dict"""
        if obj is None:
            return None

        if not isinstance(obj, dict):
            return cls.model_validate(obj)

        _obj = cls.model_validate({
            "invoice_number": obj.get("invoice_number"),
            "payment_due_date": obj.get("payment_due_date"),
            "invoice_date": obj.get("invoice_date"),
            "submission_mode": obj.get("submission_mode"),
            "recipient": Recipient.from_dict(obj["recipient"]) if obj.get("recipient") is not None else None,
            "supplier": Supplier.from_dict(obj["supplier"]) if obj.get("supplier") is not None else None,
            "invoicing_framework": InvoicingFramework.from_dict(obj["invoicing_framework"]) if obj.get("invoicing_framework") is not None else None,
            "references": InvoiceReferences.from_dict(obj["references"]) if obj.get("references") is not None else None,
            "totals": InvoiceTotals.from_dict(obj["totals"]) if obj.get("totals") is not None else None,
            "invoice_lines": [InvoiceLine.from_dict(_item) for _item in obj["invoice_lines"]] if obj.get("invoice_lines") is not None else None,
            "vat_lines": [VATLine.from_dict(_item) for _item in obj["vat_lines"]] if obj.get("vat_lines") is not None else None,
            "notes": [InvoiceNote.from_dict(_item) for _item in obj["notes"]] if obj.get("notes") is not None else None,
            "comment": obj.get("comment"),
            "current_user_id": obj.get("current_user_id"),
            "supplementary_attachments": [SupplementaryAttachment.from_dict(_item) for _item in obj["supplementary_attachments"]] if obj.get("supplementary_attachments") is not None else None,
            "payee": Payee.from_dict(obj["payee"]) if obj.get("payee") is not None else None,
            "delivery_party": DeliveryParty.from_dict(obj["delivery_party"]) if obj.get("delivery_party") is not None else None,
            "tax_representative": TaxRepresentative.from_dict(obj["tax_representative"]) if obj.get("tax_representative") is not None else None,
            "delivery_date": obj.get("delivery_date"),
            "billing_period_start": obj.get("billing_period_start"),
            "billing_period_end": obj.get("billing_period_end"),
            "payment_reference": obj.get("payment_reference"),
            "creditor_reference_id": obj.get("creditor_reference_id"),
            "direct_debit_mandate_id": obj.get("direct_debit_mandate_id"),
            "debtor_iban": obj.get("debtor_iban"),
            "payment_terms": obj.get("payment_terms"),
            "allowances_charges": [AllowanceCharge.from_dict(_item) for _item in obj["allowances_charges"]] if obj.get("allowances_charges") is not None else None,
            "additional_documents": [AdditionalDocument.from_dict(_item) for _item in obj["additional_documents"]] if obj.get("additional_documents") is not None else None,
            "buyer_accounting_reference": obj.get("buyer_accounting_reference"),
            "payment_card": PaymentCard.from_dict(obj["payment_card"]) if obj.get("payment_card") is not None else None
        })
        return _obj


