Commit inicial - upload de todos os arquivos da pasta
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
"""Resolução de competência (mês/ano) com fallback único e compartilhado.
|
||||
|
||||
Requisito: quando a competência do documento está ilegível/ausente, atribuir o
|
||||
**mês/ano corrente** (o mês da importação/lançamento) — mesma semântica que o
|
||||
antigo "primeiro dia do mês corrente", um passo mais simples (não precisa mais
|
||||
inventar um dia fictício). Tanto a extração por IA/OCR quanto o cadastro
|
||||
manual passam por aqui, para nunca divergirem.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from datetime import date
|
||||
|
||||
|
||||
_ISO_RE = re.compile(r"^(\d{4})-(\d{2})-(\d{2})$")
|
||||
_BR_RE = re.compile(r"^([0-3]?\d)[/.\-]([01]?\d)[/.\-]((?:19|20)?\d{2})$")
|
||||
|
||||
MESES = ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun", "Jul", "Ago", "Set", "Out", "Nov", "Dez"]
|
||||
|
||||
# Intervalo de anos oferecido nos seletores de competência da UI.
|
||||
ANOS_COMPETENCIA = list(range(2026, 2031))
|
||||
|
||||
|
||||
def current_competencia(reference: date | None = None) -> tuple[int, int]:
|
||||
ref = reference or date.today()
|
||||
return ref.month, ref.year
|
||||
|
||||
|
||||
def _valid_date(year: int, month: int, day: int) -> tuple[int, int] | None:
|
||||
try:
|
||||
parsed = date(year, month, day)
|
||||
except ValueError:
|
||||
return None
|
||||
return parsed.month, parsed.year
|
||||
|
||||
|
||||
def parse_competencia(value: str | None) -> tuple[int, int] | None:
|
||||
"""Tenta interpretar uma data em ISO (YYYY-MM-DD) ou BR (dd/mm/aaaa) e
|
||||
devolve apenas o (mês, ano) correspondente, descartando o dia.
|
||||
|
||||
Retorna None (sem fallback) se o valor não for uma data válida — usado
|
||||
quando a ausência de competência deve ficar visível (ex. staging).
|
||||
"""
|
||||
if not value:
|
||||
return None
|
||||
text = value.strip()
|
||||
m = _ISO_RE.match(text)
|
||||
if m:
|
||||
return _valid_date(int(m.group(1)), int(m.group(2)), int(m.group(3)))
|
||||
m = _BR_RE.match(text)
|
||||
if m:
|
||||
day, month, year = m.groups()
|
||||
year_i = int(year)
|
||||
if year_i < 100:
|
||||
year_i += 2000
|
||||
return _valid_date(year_i, int(month), int(day))
|
||||
return None
|
||||
|
||||
|
||||
def resolve_competencia(value: str | None, *, reference: date | None = None) -> tuple[int, int]:
|
||||
"""(mês, ano) válidos a partir de `value`, ou o mês/ano corrente quando
|
||||
ilegível/ausente."""
|
||||
parsed = parse_competencia(value)
|
||||
if parsed:
|
||||
return parsed
|
||||
return current_competencia(reference)
|
||||
|
||||
|
||||
def format_competencia(mes: int | None, ano: int | None) -> str:
|
||||
"""Formata mês/ano para exibição, ex. `(6, 2026)` -> "Jun/26"."""
|
||||
if not mes or not ano or not (1 <= mes <= 12):
|
||||
return ""
|
||||
return f"{MESES[mes - 1]}/{ano % 100:02d}"
|
||||
Reference in New Issue
Block a user