
    lj                        S r SSKrSrSr\R                  " SS5      r\" SSS/S	/\S
5      \" SSS/S/\S
5      \" SSS/S/\S
5      \" SSS/S/\S
5      \" SSS/S/\S5      \" SSS/S/\S5      \" SSS// SQ\S5      \" SS / S!/\S5      \" S"S#/ S$S%/\S5      /	rS"r\ V s0 s H  o R                  U _M     sn r	S& r
S,S' jrS-S( jrS) rS.S* jrS+ rgs  sn f )/u  Канон доходных строк Марса — единственный источник правды.

Раньше справочник был размазан по трём местам и расходился: финмодель (12 строк),
плановые операции Финтабло (6), направления AlfaCRM (7). Одно и то же понятие
называлось по-разному, а «Съёмки» в CRM и «Мероприятия внешние» в финмодели
вообще про разное (см. docs/income-canon-design-2026-07-31.md).

Канон — направления CRM: только у них есть измеримый факт. Мерч и внешние мероприятия
собираются в строку «Прочее». Онлайн-подписка стоит отдельной строкой с нулевым фактом:
клуб не запущен, направления в CRM нет, но строка заведена заранее — чтобы при запуске
хватило прописать направление, не перестраивая канон, планки и два листа.
    Ni i Linez,slug name crm_dirs model_rows category riskyurokiz1-1   Fgruppyu   Групповые   horu   Хор   arendau   Аренда   iventyu+   Внутренние мероприятия	   TcoverzCover sessionu   Съёмки
   b2bB2B)         podpiskau   Онлайн-подписка   procheeu   Прочее      c                     [         U    $ )u   Строка канона по slug. KeyError, если slug неизвестен — молчать нельзя,
иначе прогноз уедет в никуда.)_BY_SLUG)slugs    "/home/rasp/finsync/income_lines.pyby_slugr   /   s     D>    c                 v   ^ [        U4S jU R                   5       5      nU R                  [        :X  a  X2-   $ U$ )u_  Факт одной строки канона. Чистая функция — её и тестируем без сети.

Строки без направлений дают 0 (онлайн-подписка, пока клуб не запущен), кроме
OTHER_SLUG: туда сваливается доход CRM вне направлений.
c              3   H   >#    U  H  nTR                  US 5      v   M     g7f)        N)get).0ddirss     r   	<genexpr>crm_fact_for.<locals>.<genexpr>;   s     61dhhq#s   ")sumcrm_dirsr   
OTHER_SLUG)liner&   othervals    `  r   crm_fact_forr/   5   s1     66
6C))z13;:s:r   c           	          SSK nUR                  XU5      u  pE[         Vs0 s H  ofR                  [	        XdU5      _M     sn$ s  snf )u^  {slug: сумма} — факт месяца mm (MM.YYYY) из AlfaCRM по канону.

«Прочее» — весь доход CRM, не попавший ни в одно направление (мерч, внешние
мероприятия): margins сознательно держит его отдельно, см. INCOME_EXCLUDE_NAMES.

pay_items — справочник статей оплаты AlfaCRM; передавай его, если считаешь факт
за несколько месяцев подряд (напр. plan_fact.py по всем месяцам года). Без него
margins.income_with_other лезет за этим справочником в AlfaCRM на каждый вызов —
лишний запрос на каждый месяц там, где список один и тот же весь прогон.
r   N)marginsincome_with_otherLINESr   r/   )mmtoken	pay_itemsr1   r&   r-   ls          r   crm_factr8   ?   sA     ++ByAKD:?@%QFFL%00%@@@s    Ac           	         SSK nUR                   Vs0 s H  o"U R                  US5      _M     nn[        U R                  S0 5      R	                  5       5      n[
         Vs0 s H  oUR                  [        XSU5      _M     sn$ s  snf s  snf )u  {slug: сумма} — то же самое, что crm_fact(), но из уже посчитанных данных
margins.income_all_months() (один проход по AlfaCRM на весь год), а не отдельного
HTTP-похода на каждый месяц.

income_m — запись ОДНОГО месяца из результата income_all_months (плоский словарь
{направление: сумма} + служебные ключи "__crm_other__"/"__income_detail__", см.
margins._collect_income). Используй при прогоне по многим месяцам подряд
(plan_fact.py по всем 12 месяцам года) — раньше там на каждый месяц заново гоняли
полную пагинацию /v2api/{br}/pay/index по всем филиалам (см. находку #4 финального
ревью), хотя income_all_months уже даёт то же самое одним проходом.
r   Nr"   __crm_other__)r1   
DIRECTIONSr#   r)   valuesr3   r   r/   )income_mr1   r%   r&   r-   r7   s         r   crm_fact_from_month_datar>   O   s{     -4-?-?@-?x||As##-?D@_b188:;E:?@%QFFL%00%@@ A@s   B" B
c                 r   SSK nUc  UR                  SS9nSU -  n0 nU H  nUR                  S5      =(       d    SnUR                  S5      (       d  M5  UR                  U5      (       d  MM  U[	        U5      S nUR                  US	5      [        UR                  S
5      =(       d    S5      -   XG'   M     U$ )u  {slug: сумма} — прогноз месяца mm из плановых операций Финтабло.

Пусто ({}), если планок на месяц нет: это норма (дальние месяцы считаются
по финмодели), а не сбой.
r   Nincome)groupzplansync:income:%s:
externalId isPlanr"   value)coreft_transactionsr#   
startswithlenfloat)r4   
all_incomerF   prefixrestextr   s           r   plan_from_fintablorP   a   s     )))9
"R'F
CeeL!'R55??s~~f55s6{|$Dc*U155>3FQ-GGCI	 
 Jr   c           
          SSK n[         Vs0 s H*  oDR                  UR                  XUR                  U5      _M,     sn$ s  snf )u   {slug: сумма} — план месяца ym=(год, месяц) из финмодели.

plan/colidx — то, что отдают cashflow.read_plan() и cashflow.month_columns().
r   N)cashflowr3   r   pv
model_rows)plancolidxymrR   r7   s        r   plan_from_modelrX   t   s7    
 INOAFFHKKallB??OOOs   1A)r"   )NN)N)__doc__collectionsCAT_B2CCAT_B2B
namedtupler   r3   r+   r   r   r   r/   r8   r>   rP   rX   )r7   s   0r   <module>r^      sz    
 f&TU 	Eug1#X_afg(8L7M]^\_jqsxyH(QRPS^eglmN^4DWXVYdkmrsADqCruvtw  CJ  LP  QO~.>QSPT^egklEug,X_aef 	4BYZX[fmostNBQRTVPW^egkl	 
$%u!FFAIu%;A A$&PQ &s   $C