# coding: utf-8

"""
    BeeL Public API

    Public invoicing API for self-employed professionals in Spain with full VeriFactu support.  **Authentication:** This API uses **API Keys** for authentication. Endpoints are designed for external integrations, automation, and programmatic access.  For browser-based authentication, use the Private API (with Session Cookies).  **Key Features:** - VeriFactu regulatory compliance - Complete invoice management (standard, corrective, simplified) - Customer management - Tax and invoice series configuration - PDF generation - Email sending - Duplicate prevention (idempotency)  **Idempotency:** Creation endpoints (POST) support the `Idempotency-Key` header to prevent duplicates in case of retries.  **Identifiers:** - All resources use UUIDs for technical identification - Invoice numbers are human-readable and searchable 

    The version of the OpenAPI document: 1.0.0
    Contact: it@beel.es
    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 datetime import date, datetime
from pydantic import BaseModel, ConfigDict, Field, StrictInt, StrictStr
from typing import Any, ClassVar, Dict, List, Optional
from typing_extensions import Annotated
from beel_generated.models.generation_action import GenerationAction
from beel_generated.models.invoice_attachments_inner import InvoiceAttachmentsInner
from beel_generated.models.invoice_line import InvoiceLine
from beel_generated.models.invoice_sending_history_inner import InvoiceSendingHistoryInner
from beel_generated.models.invoice_status import InvoiceStatus
from beel_generated.models.invoice_totals import InvoiceTotals
from beel_generated.models.invoice_type import InvoiceType
from beel_generated.models.issuer_data import IssuerData
from beel_generated.models.payment_info import PaymentInfo
from beel_generated.models.recipient_data import RecipientData
from beel_generated.models.rectification_type import RectificationType
from beel_generated.models.series_info import SeriesInfo
from beel_generated.models.veri_factu import VeriFactu
from beel_generated.models.veri_factu_rectification_code import VeriFactuRectificationCode
from typing import Optional, Set
from typing_extensions import Self

class Invoice(BaseModel):
    """
    Invoice
    """ # noqa: E501
    id: StrictStr = Field(description="Unique invoice UUID")
    invoice_number: Optional[StrictStr] = Field(default=None, description="Complete invoice number (series + sequential)")
    series: SeriesInfo
    number: Optional[StrictInt] = Field(default=None, description="Sequential number within the series")
    type: InvoiceType
    status: InvoiceStatus
    issue_date: date = Field(description="Invoice issue date")
    due_date: Optional[date] = Field(default=None, description="Payment due date")
    payment_date: Optional[date] = Field(default=None, description="Business date when the payment was received (e.g., the date on the bank statement). Set by the user when marking the invoice as paid. Only present when status is PAID. Contrast with `paid_at`, which is the system timestamp of when the status change was recorded. ")
    sent_at: Optional[datetime] = Field(default=None, description="System timestamp when the invoice was marked as sent. Present when status is SENT or later. ")
    paid_at: Optional[datetime] = Field(default=None, description="System timestamp when the payment was recorded in the system. Automatically set when the invoice status changes to PAID. Contrast with `payment_date`, which is the business date chosen by the user. ")
    scheduled_for: Optional[date] = Field(default=None, description="Date when the invoice should be automatically processed. Only present when status is SCHEDULED. ")
    scheduled_action: Optional[GenerationAction] = None
    issuer: IssuerData
    recipient: RecipientData
    lines: Annotated[List[InvoiceLine], Field(min_length=1)]
    totals: InvoiceTotals
    payment_info: Optional[PaymentInfo] = None
    notes: Optional[Annotated[str, Field(strict=True, max_length=1000)]] = Field(default=None, description="Additional observations or notes")
    rectified_invoice_id: Optional[StrictStr] = Field(default=None, description="UUID of the invoice being rectified (only for corrective invoices)")
    rectification_reason: Optional[Annotated[str, Field(strict=True, max_length=500)]] = Field(default=None, description="Reason for rectification (only for corrective invoices)")
    recurring_invoice_id: Optional[StrictStr] = Field(default=None, description="UUID of the recurring invoice that generated this invoice (if any)")
    recurring_invoice_name: Optional[StrictStr] = Field(default=None, description="Name of the recurring invoice (denormalized for display)")
    rectification_type: Optional[RectificationType] = None
    rectification_code: Optional[VeriFactuRectificationCode] = None
    metadata: Optional[Dict[str, Any]] = Field(default=None, description="Additional metadata in key-value format. Useful examples: - stripe_payment_id: Stripe payment ID - stripe_charge_id: Stripe charge ID - external_order_id: External order ID - payment_platform: Payment platform used - internal_notes: Internal notes not visible to customer ")
    pdf_download_url: Optional[StrictStr] = Field(default=None, description="Relative URL to download the PDF via the API endpoint. Path is relative to the API base URL (e.g., https://api.beel.es/api). Only present when the invoice has a generated PDF. ")
    verifactu: Optional[VeriFactu] = None
    attachments: Optional[List[InvoiceAttachmentsInner]] = None
    sending_history: Optional[List[InvoiceSendingHistoryInner]] = None
    created_at: datetime
    updated_at: datetime
    deleted_at: Optional[datetime] = None
    __properties: ClassVar[List[str]] = ["id", "invoice_number", "series", "number", "type", "status", "issue_date", "due_date", "payment_date", "sent_at", "paid_at", "scheduled_for", "scheduled_action", "issuer", "recipient", "lines", "totals", "payment_info", "notes", "rectified_invoice_id", "rectification_reason", "recurring_invoice_id", "recurring_invoice_name", "rectification_type", "rectification_code", "metadata", "pdf_download_url", "verifactu", "attachments", "sending_history", "created_at", "updated_at", "deleted_at"]

    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 Invoice 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 series
        if self.series:
            _dict['series'] = self.series.to_dict()
        # override the default output from pydantic by calling `to_dict()` of issuer
        if self.issuer:
            _dict['issuer'] = self.issuer.to_dict()
        # 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 each item in lines (list)
        _items = []
        if self.lines:
            for _item_lines in self.lines:
                if _item_lines:
                    _items.append(_item_lines.to_dict())
            _dict['lines'] = _items
        # 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 payment_info
        if self.payment_info:
            _dict['payment_info'] = self.payment_info.to_dict()
        # override the default output from pydantic by calling `to_dict()` of verifactu
        if self.verifactu:
            _dict['verifactu'] = self.verifactu.to_dict()
        # override the default output from pydantic by calling `to_dict()` of each item in attachments (list)
        _items = []
        if self.attachments:
            for _item_attachments in self.attachments:
                if _item_attachments:
                    _items.append(_item_attachments.to_dict())
            _dict['attachments'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in sending_history (list)
        _items = []
        if self.sending_history:
            for _item_sending_history in self.sending_history:
                if _item_sending_history:
                    _items.append(_item_sending_history.to_dict())
            _dict['sending_history'] = _items
        # set to None if sent_at (nullable) is None
        # and model_fields_set contains the field
        if self.sent_at is None and "sent_at" in self.model_fields_set:
            _dict['sent_at'] = None

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

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

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

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

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

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

        return _dict

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

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

        _obj = cls.model_validate({
            "id": obj.get("id"),
            "invoice_number": obj.get("invoice_number"),
            "series": SeriesInfo.from_dict(obj["series"]) if obj.get("series") is not None else None,
            "number": obj.get("number"),
            "type": obj.get("type"),
            "status": obj.get("status"),
            "issue_date": obj.get("issue_date"),
            "due_date": obj.get("due_date"),
            "payment_date": obj.get("payment_date"),
            "sent_at": obj.get("sent_at"),
            "paid_at": obj.get("paid_at"),
            "scheduled_for": obj.get("scheduled_for"),
            "scheduled_action": obj.get("scheduled_action"),
            "issuer": IssuerData.from_dict(obj["issuer"]) if obj.get("issuer") is not None else None,
            "recipient": RecipientData.from_dict(obj["recipient"]) if obj.get("recipient") is not None else None,
            "lines": [InvoiceLine.from_dict(_item) for _item in obj["lines"]] if obj.get("lines") is not None else None,
            "totals": InvoiceTotals.from_dict(obj["totals"]) if obj.get("totals") is not None else None,
            "payment_info": PaymentInfo.from_dict(obj["payment_info"]) if obj.get("payment_info") is not None else None,
            "notes": obj.get("notes"),
            "rectified_invoice_id": obj.get("rectified_invoice_id"),
            "rectification_reason": obj.get("rectification_reason"),
            "recurring_invoice_id": obj.get("recurring_invoice_id"),
            "recurring_invoice_name": obj.get("recurring_invoice_name"),
            "rectification_type": obj.get("rectification_type"),
            "rectification_code": obj.get("rectification_code"),
            "metadata": obj.get("metadata"),
            "pdf_download_url": obj.get("pdf_download_url"),
            "verifactu": VeriFactu.from_dict(obj["verifactu"]) if obj.get("verifactu") is not None else None,
            "attachments": [InvoiceAttachmentsInner.from_dict(_item) for _item in obj["attachments"]] if obj.get("attachments") is not None else None,
            "sending_history": [InvoiceSendingHistoryInner.from_dict(_item) for _item in obj["sending_history"]] if obj.get("sending_history") is not None else None,
            "created_at": obj.get("created_at"),
            "updated_at": obj.get("updated_at"),
            "deleted_at": obj.get("deleted_at")
        })
        return _obj


