119 lines
4.5 KiB
Python
119 lines
4.5 KiB
Python
"""Состав группы компаний (спека §4.2): члены из словаря, ИНН по префиксу, доли, компании без ИНН в ОСВ."""
|
||
|
||
from dataclasses import dataclass
|
||
from decimal import Decimal
|
||
|
||
from synthgen import inn
|
||
from synthgen.config import Config
|
||
from synthgen.dictionary import Org
|
||
from synthgen.rng import rng_for, stable_hash
|
||
|
||
_HASH_RANGE = 90000
|
||
_CENT = Decimal("0.01")
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class Member:
|
||
code: str # C01, C02, …
|
||
prefix: str # префикс ГК
|
||
org: Org # запись словаря, давшая корень имени
|
||
name: str # АО "<prefix> <корень>"
|
||
name_1c: str # <prefix> <корень> АО — как 1С пишет контрагента
|
||
inn: str
|
||
kpp: str
|
||
is_head: bool
|
||
head_share: Decimal | None # доля головной в дочерней; у головной None
|
||
minority: Org | None # миноритарий дочерней (внешний держатель остальной доли)
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class Group:
|
||
prefix: str
|
||
name: str # ГК <prefix>
|
||
gk_id: str # <prefix>-000001
|
||
members: list[Member]
|
||
no_inn: frozenset[str] # коды компаний, чья ОСВ записана без ИНН
|
||
variant_name: dict[str, str] # код ОСВ-компании без ИНН → код члена группы, записанного другим написанием
|
||
externals: list[Org] # словарь без банков и без выбранных в группу
|
||
banks: list[Org]
|
||
|
||
|
||
def member_inn(prefix: str, i: int) -> str:
|
||
"""ИНН i-й компании группы (i от 1, < 100): номер = 1000000 + 100 × h(prefix) + i."""
|
||
return inn.make_inn10(1000000 + 100 * stable_hash(prefix, _HASH_RANGE) + i)
|
||
|
||
|
||
def member_kpp(i: int) -> str:
|
||
return f"00{i:02d}01001"
|
||
|
||
|
||
def variant_spelling(member: Member) -> str:
|
||
"""Вариантное написание члена группы в ОСВ без ИНН: корень, префикс, форма без кавычек (решение 17)."""
|
||
return f"{member.org.root} {member.prefix} {member.org.form}"
|
||
|
||
|
||
def build_group(cfg: Config, orgs: list[Org]) -> Group:
|
||
rng = rng_for(cfg.seed, "group", cfg.prefix)
|
||
candidates = [o for o in orgs if o.form in ("ООО", "АО") and o.kind != "банк"]
|
||
heads = [o for o in candidates if o.form == "АО"]
|
||
head_org = rng.choice(heads)
|
||
rest = [o for o in candidates if o is not head_org]
|
||
chosen = [head_org, *rng.sample(rest, cfg.group_size - 1)]
|
||
|
||
children = list(range(1, cfg.group_size))
|
||
with_minority = set(rng.sample(children, min(len(children), rng.randint(1, 2)))) if children else set()
|
||
chosen_set = {o.inn for o in chosen}
|
||
pool = [o for o in orgs if o.kind != "банк" and o.form != "ИП" and o.inn not in chosen_set]
|
||
|
||
members: list[Member] = []
|
||
for idx, org in enumerate(chosen):
|
||
i = idx + 1
|
||
share: Decimal | None = None
|
||
minority: Org | None = None
|
||
if idx > 0:
|
||
if idx in with_minority:
|
||
share = (Decimal(rng.randint(51, 99)) / 100).quantize(_CENT)
|
||
minority = rng.choice(pool)
|
||
else:
|
||
share = Decimal("1.00")
|
||
members.append(
|
||
Member(
|
||
code=f"C{i:02d}",
|
||
prefix=cfg.prefix,
|
||
org=org,
|
||
name=f'{org.form} "{cfg.prefix} {org.root}"',
|
||
name_1c=f"{cfg.prefix} {org.root} {org.form}",
|
||
inn=member_inn(cfg.prefix, i),
|
||
kpp=member_kpp(i),
|
||
is_head=idx == 0,
|
||
head_share=share,
|
||
minority=minority,
|
||
)
|
||
)
|
||
|
||
codes = [m.code for m in members]
|
||
child_codes = codes[1:]
|
||
n = cfg.no_inn_companies
|
||
picked = rng.sample(child_codes, min(n, len(child_codes)))
|
||
if n > len(child_codes):
|
||
picked.append(codes[0])
|
||
no_inn = frozenset(picked)
|
||
|
||
variant_name: dict[str, str] = {}
|
||
for code in sorted(no_inn):
|
||
others = [c for c in codes if c != code]
|
||
variant_name[code] = rng.choice(others)
|
||
|
||
externals = [o for o in orgs if o.kind != "банк" and o.inn not in chosen_set]
|
||
banks = [o for o in orgs if o.kind == "банк"]
|
||
return Group(
|
||
prefix=cfg.prefix,
|
||
name=f"ГК {cfg.prefix}",
|
||
gk_id=f"{cfg.prefix}-000001",
|
||
members=members,
|
||
no_inn=no_inn,
|
||
variant_name=variant_name,
|
||
externals=externals,
|
||
banks=banks,
|
||
)
|