Source code for ebarimt_pos_sdk.resources.rest.receipt.schema

from __future__ import annotations

from datetime import datetime, timezone
from decimal import Decimal
from typing import Any, Literal, TypeAlias

from pydantic import field_serializer, field_validator

from ...base_model import BaseEbarimtModel
from ...enum import (
    BarCodeType,
    PaymentCode,
    PaymentStatus,
    ReceiptCreateStatus,
    ReceiptType,
    TaxType,
)

# Json building block types
Number = Decimal | int | float


_ReceiptType: TypeAlias = (
    ReceiptType | Literal["B2C_RECEIPT", "B2B_RECEIPT", "B2C_INVOICE", "B2B_INVOICE"]
)

_TaxType = TaxType | Literal["VAT_ABLE", "VAT_FREE", "VAT_ZERO", "NOT_VAT"]

_BarCodeType: TypeAlias = BarCodeType | Literal["UNDEFINED", "GS1", "ISBN"]

_PaymentCode: TypeAlias = PaymentCode | Literal["CASH", "PAYMENT_CARD"]

_PaymentStatus: TypeAlias = PaymentStatus | Literal["PAID", "PAY", "REVERSED", "ERROR"]

_ReceiptCreateStatus = ReceiptCreateStatus | Literal["SUCCESS", "ERROR", "PAYMENT"]


[docs] class ReceiptItemData(BaseEbarimtModel): """ items[].data Note: docs show lotNo + stockQR, but examples also show nested "data": {"stockQR":[...]} sometimes. This model follows the schema: lotNo, stockQR directly under data. """ lotNo: str | None = None stockQR: list[str] | None = None
[docs] class Item(BaseEbarimtModel): """Single product or service included in a receipt.""" name: str measure_unit: str qty: Number unit_price: Number total_amount: Number
[docs] @field_validator("qty", "unit_price") @classmethod def validate_positive(cls, v: Number) -> Number: if v <= 0: raise ValueError("Must be greater than 0") return v
bar_code: str | None = None bar_code_type: _BarCodeType | None = None classification_code: str | None = None tax_product_code: str | None = None total_vat: Number | None = None total_city_tax: Number | None = None data: ReceiptItemData | None = None
[docs] class SubReceipt(BaseEbarimtModel): """Sub-receipt containing items, tax info, and seller details.""" total_amount: Number tax_type: _TaxType merchant_tin: str items: list[Item]
[docs] @field_validator("items") @classmethod def validate_items_not_empty(cls, v: list[Item]) -> list[Item]: if not v: raise ValueError("items must not be empty") return v
total_vat: Number | None = None total_city_tax: Number | None = None customer_tin: str | None = None bank_account_no: str | None = None iban: str | None = None invoice_id: str | None = None data: dict[str, Any] | None = None
class SubReceiptResponse(SubReceipt): id: str
[docs] class PaymentCardData(BaseEbarimtModel): """Card payment metadata when using PAYMENT_CARD.""" terminal_id: str rrn: str masked_card_number: str easy: bool bank_code: str | None = None
[docs] class Payment(BaseEbarimtModel): """Payment information associated with a receipt.""" code: _PaymentCode status: _PaymentStatus paid_amount: Number exchange_code: str | None = None data: PaymentCardData | None = None # Only when code is PAYMENT_CARD
class _CreateReceiptBase(BaseEbarimtModel): """Base model for creating or returning a receipt.""" branch_no: str total_amount: Number merchant_tin: str pos_no: str type: _ReceiptType # Need to define receipts: list[ReceiptBase] total_vat: Number | None = None total_city_tax: Number | None = None district_code: str | None = None customer_tin: str | None = None consumer_no: str | None = None inactive_id: str | None = None invoice_id: str | None = None report_month: str | None = None data: dict[str, Any] | None = None payments: list[Payment] | None = None
[docs] class CreateReceiptRequest(_CreateReceiptBase): """Request payload for creating a new receipt.""" bill_id_suffix: str receipts: list[SubReceipt]
[docs] class CreateReceiptResponse(_CreateReceiptBase): """Response returned after receipt creation.""" id: str version: str pos_id: int status: _ReceiptCreateStatus qr_data: str lottery: str date: datetime easy: bool receipts: list[SubReceiptResponse]
[docs] @field_validator("date", mode="before") @classmethod def parse_date(cls, v: str | datetime) -> datetime: if isinstance(v, datetime): return v if v.tzinfo else v.replace(tzinfo=timezone.utc) return datetime.strptime(v, "%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone.utc)
[docs] class DeleteReceiptRequest(BaseEbarimtModel): """Request payload for deleting an existing receipt.""" id: str date: datetime | None = None
[docs] @field_serializer("date") def serialize_date(self, value: datetime | None) -> str | None: # Ebarimt expects: yyyy-mm-dd hh:mm:ss if value is None: return value return value.strftime("%Y-%m-%d %H:%M:%S")