work-license-server 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
  - **Start** — `10 000 ₽ / год` (до 12 обращений в год на место, реакция до 8 рабочих часов);
13
13
  - **Business** — `15 000 ₽ / год` (до 48 обращений, реакция до 2 часов);
14
14
  - **Enterprise** — `20 000 ₽ / год` (безлимит, реакция до 15 минут 24/7).
15
- 3. **Региональная франшиза** — интерактивная карта всех субъектов РФ (`assets/russia-regions.json`, границы © участники OpenStreetMap), квота франшиз пропорциональна населению, стоимость — `5 000 000 ₽ ×` индекс субъекта, резерв — невозвратный залог `100 000 ₽` (`POST /api/partners/reserve`).
15
+ 3. **Региональная франшиза** — интерактивная карта всех субъектов РФ (`assets/russia-regions.json`, границы © участники OpenStreetMap), квота франшиз пропорциональна населению, паушальный взнос — `500 000 ₽ ×` индекс субъекта, резерв — невозвратный залог `100 000 ₽` (`POST /api/partners/reserve`), полная оплата паушального взноса — в течение 1 года.
16
16
  4. **ЮKassa** (`/api/orders/checkout`, `/api/payments/yookassa/webhook`) — чеки 54-ФЗ, выпуск и продление сертификатов `Ed25519` (`WORK-LIC-v1`) с привязкой к `HWID` и ключу узла `#system/node-key.json`.
17
17
  5. **Боты поддержки** — `/api/bots/max/webhook`, `/api/bots/telegram/webhook`, `/api/bots/vk/webhook`.
18
18
  6. **ИИ-агент** — `POST /api/agent/chat`: консультации, расчёт лицензий, вакантные франшизы по субъекту, контакты.
package/assets/landing.js CHANGED
@@ -520,7 +520,7 @@
520
520
  <div class="r"><span>Округ</span><em>${esc(r.district)}</em></div>
521
521
  <div class="r"><span>Население</span><em>${String(r.populationMillions).replace('.', ',')} млн</em></div>
522
522
  <div class="r"><span>Вакантно</span><em style="color:${vac > 0 ? 'var(--ok)' : 'var(--danger)'}">${vac} из ${q}</em></div>
523
- <div class="r"><span>Франшиза</span><em>${rub(feeOf(r))}</em></div>`;
523
+ <div class="r"><span>Паушальный взнос</span><em>${rub(feeOf(r))}</em></div>`;
524
524
  const br = box.getBoundingClientRect();
525
525
  let x = e.clientX - br.left + 16, y = e.clientY - br.top + 16;
526
526
  const tw = tip.offsetWidth || 200, th = tip.offsetHeight || 110;
@@ -772,7 +772,7 @@
772
772
  <td><b>${esc(r.reservationId)}</b><div class="small muted">Резерв франшизы</div></td>
773
773
  <td><b>${esc(r.regionName)}</b> <span class="muted">(${esc(r.regionCode)})</span></td>
774
774
  <td><b>${esc(r.companyName)}</b><div class="small muted">ИНН ${esc(r.inn || '—')} · ${esc(r.email || '—')}</div></td>
775
- <td>Франшиза <b>${rub(r.franchiseFeeRub)}</b><div class="small muted">Залог ${rub(r.depositAmountRub || D.franchise.reservationDepositRub)}</div></td>
775
+ <td>Паушальный взнос <b>${rub(r.franchiseFeeRub)}</b><div class="small muted">Залог ${rub(r.nonRefundableDepositRub || r.depositAmountRub || D.franchise.reservationDepositRub)} · полная оплата за 1 год</div></td>
776
776
  <td><span class="tag ${r.depositPaid ? 'ok' : ''}">${r.depositPaid ? 'залог оплачен' : 'ждёт оплаты залога'}</span></td>
777
777
  <td><div style="display:flex;gap:6px;flex-wrap:wrap">
778
778
  ${!r.depositPaid && r.orderId ? `<button class="btn btn-ok btn-sm" data-pay-order="${esc(r.orderId)}">Оплатить залог</button>` : ''}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "work-license-server",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Лицензионный сервер, биллинг ЮKassa, боты поддержки (MAX, Telegram, VK) и лендинг ПАК WORK (work.odant.org)",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/agent.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * - комплекс ODANT-WORK на AMD Ryzen™ AI Max+ PRO 495 + Radeon™ 8065S;
6
6
  * - ИИ-консультант и мультиагентная система платформы (ролевые агенты, навыки, оркестрация);
7
7
  * - докупка годовых лицензий (Start 10 000 ₽, Business 15 000 ₽, Enterprise 20 000 ₽) со скидкой от количества;
8
- * - вакантные франшизы по любому субъекту РФ (залог 100 000 ₽, базовая стоимость 5 000 000 ₽);
8
+ * - вакантные франшизы по любому субъекту РФ (залог 100 000 ₽, базовая стоимость 500 000 ₽);
9
9
  * - контакты разработчика платформы.
10
10
  */
11
11
  import {
@@ -70,15 +70,15 @@ export class LicenseServerAiAgent {
70
70
 
71
71
  // 1. Франшиза в конкретном субъекте РФ
72
72
  const region = findRegionInText(text);
73
- if (region && /(франш|регион|вакант|квот|резерв|залог|партн|сколько|област|край|республ)/i.test(lower)) {
73
+ if (region && /(франш|паушал|взнос|регион|вакант|квот|резерв|залог|партн|сколько|област|край|республ)/i.test(lower)) {
74
74
  const q = this.store.getRegionPartnerQuotaStatus(region.code);
75
75
  return {
76
76
  reply: [
77
77
  `**${q.regionName}** (${q.district})`,
78
78
  `• Население: **${String(q.populationMillions).replace('.', ',')} млн чел.**`,
79
79
  `• Вакантных франшиз: **${q.vacantFranchises} из ${q.maxPartnersQuota}** — квота пропорциональна населению`,
80
- `• Стоимость франшизы: **${fmt(q.franchiseFeeRub)} ₽** (база ${fmt(FRANCHISE_PROGRAM.baseFranchiseFeeRub)} ₽ × индекс ${q.economicIndex})`,
81
- `• Невозвратный залог за резерв: **${fmt(q.reservationDepositRub)} ₽**`,
80
+ `• Паушальный взнос: **${fmt(q.franchiseFeeRub)} ₽** (базовый взнос ${fmt(FRANCHISE_PROGRAM.baseFranchiseFeeRub)} ₽ × индекс ${q.economicIndex})`,
81
+ `• Невозвратный залог за резерв: **${fmt(q.reservationDepositRub)} ₽**, полная оплата остатка (**${fmt(q.remainingAfterDepositRub)} ₽**) — **в течение 1 года**`,
82
82
  `• Скидка партнёра: от ${FRANCHISE_PROGRAM.minDiscountPercent}% до ${FRANCHISE_PROGRAM.maxDiscountPercent}% от годового объёма продаж`,
83
83
  q.vacantFranchises > 0
84
84
  ? 'Я выделил субъект на карте — можно сразу нажать «Зарезервировать».'
@@ -164,14 +164,14 @@ export class LicenseServerAiAgent {
164
164
  }
165
165
 
166
166
  // 6. Партнёрская программа в целом
167
- if (/(франш|партн|залог|резерв|обучен|сертифик|nas|инференс)/i.test(lower)) {
167
+ if (/(франш|паушал|взнос|партн|залог|резерв|обучен|сертифик|nas|инференс)/i.test(lower)) {
168
168
  const all = this.store.listRegionsWithQuotas().filter(r => !r.hidden);
169
169
  const totalVacant = all.reduce((s, r) => s + r.vacantFranchises, 0);
170
170
  return {
171
171
  reply: [
172
172
  '**Региональная франшиза ODANT-WORK:**',
173
- `• Базовая стоимость: **${fmt(FRANCHISE_PROGRAM.baseFranchiseFeeRub)} ₽** × экономический индекс субъекта`,
174
- `• Резерв субъекта: невозвратный залог **${fmt(FRANCHISE_PROGRAM.reservationDepositRub)} ₽** через ЮKassa`,
173
+ `• Базовый паушальный взнос: **${fmt(FRANCHISE_PROGRAM.baseFranchiseFeeRub)} ₽** × экономический индекс субъекта`,
174
+ `• Резерв субъекта: невозвратный залог **${fmt(FRANCHISE_PROGRAM.reservationDepositRub)} ₽** через ЮKassa, полная оплата паушального взноса — **в течение 1 года**`,
175
175
  `• Сейчас свободно **${totalVacant}** франшиз в **${all.length}** субъектах РФ (квота пропорциональна населению)`,
176
176
  `• Скидка партнёра: от ${FRANCHISE_PROGRAM.minDiscountPercent}% до ${FRANCHISE_PROGRAM.maxDiscountPercent}% от годового объёма продаж`,
177
177
  '• 100% дохода партнёра: оборудование и гарантия на него, NAS для резервных копий, серверы инференса, собственный API к моделям, поддержка, дообучение агентов и навыков.',
@@ -186,7 +186,7 @@ export class LicenseServerAiAgent {
186
186
  'Я ИИ-агент лицензионного центра **ODANT-WORK**. Коротко об условиях:',
187
187
  `1. **Комплекс** — ${fmt(PAK_PRODUCT.basePriceRub)} ₽: AMD Ryzen™ AI Max+ PRO 495, Radeon™ 8065S, 192 ГБ, 4 ТБ, ${PAK_PRODUCT.baseSeats} вечных мест и год поддержки.`,
188
188
  '2. **Доп. места на год:** Start 10 000 ₽ · Business 15 000 ₽ · Enterprise 20 000 ₽, скидка за количество до −25%.',
189
- `3. **Франшиза:** от ${fmt(FRANCHISE_PROGRAM.baseFranchiseFeeRub)} ₽ × индекс субъекта, залог за резерв ${fmt(FRANCHISE_PROGRAM.reservationDepositRub)} ₽.`,
189
+ `3. **Франшиза:** паушальный взнос ${fmt(FRANCHISE_PROGRAM.baseFranchiseFeeRub)} ₽ × индекс субъекта, залог за резерв ${fmt(FRANCHISE_PROGRAM.reservationDepositRub)} ₽, полная оплата в течение 1 года.`,
190
190
  'Спросите, например: «Рассчитай 25 мест Business», «Франшиза в Рязанской области» или «Что умеют ИИ-агенты?»',
191
191
  ].join('\n'),
192
192
  actions: [],
package/src/catalog.js CHANGED
@@ -277,14 +277,16 @@ export function calculateOrderQuote({
277
277
 
278
278
  /**
279
279
  * Параметры партнёрской программы и региональной франшизы ODANT-WORK:
280
- * - Предварительная базовая стоимость франшизы = 5 000 000 ₽ (корректируется пропорционально экономическому индексу региона).
281
- * - Сразу при резервировании франшизы вносится невозвратный залог = 100 000 ₽.
280
+ * - Базовый паушальный взнос = 500 000 ₽ (корректируется пропорционально экономическому индексу региона).
281
+ * - Сразу при резервировании франшизы вносится невозвратный залог = 100 000 ₽, полная оплата паушального взноса — в течение 1 года (12 мес.).
282
282
  * - Максимальное количество франшиз в регионе пропорционально населению региона (partnersPerMillion * populationMillions, мин. 1).
283
283
  * - Партнёрская скидка растёт пропорционально годовому объёму продаж от начальных 10% до максимальных 50%.
284
284
  */
285
285
  export const FRANCHISE_PROGRAM = Object.freeze({
286
- baseFranchiseFeeRub: 5_000_000, // Предварительная базовая стоимость франшизы (5 млн ₽ при экономическом индексе 1.0)
286
+ baseFranchiseFeeRub: 500_000, // Базовый паушальный взнос (500 тыс. ₽ при экономическом индексе 1.0)
287
287
  reservationDepositRub: 100_000, // Невозвратный залог сразу за резервирование региональной франшизы (100 тыс. ₽)
288
+ fullPaymentPeriodMonths: 12, // Срок полной оплаты паушального взноса — в течение 1 года (12 месяцев)
289
+ fullPaymentPeriodYears: 1, // 1 год на полную оплату паушального взноса после резервирования
288
290
  partnersPerMillionPopulation: 1, // Норматив квоты партнёров: 1 франшиза на 1 млн населения региона
289
291
  minDiscountPercent: 10, // Начальная партнёрская скидка (10%)
290
292
  maxDiscountPercent: 50, // Максимальная партнёрская скидка (50%)
@@ -588,6 +590,8 @@ export function calculateFranchiseFee(regionCodeOrIndex = 'RU-SVE', customPopula
588
590
  franchiseFeeRub,
589
591
  reservationDepositRub,
590
592
  remainingAfterDepositRub: Math.max(0, franchiseFeeRub - reservationDepositRub),
593
+ fullPaymentPeriodMonths: FRANCHISE_PROGRAM.fullPaymentPeriodMonths,
594
+ fullPaymentPeriodYears: FRANCHISE_PROGRAM.fullPaymentPeriodYears,
591
595
  };
592
596
  }
593
597
 
package/src/landing.js CHANGED
@@ -420,12 +420,12 @@ export function renderLandingPage({ publicKey = '', regions = null, assetVersion
420
420
  <section class="stage" id="stage-franchise">
421
421
  <div class="sec-head">
422
422
  <div><span class="kicker">Партнёрская программа</span><h2 class="h2">Региональная франшиза ODANT-WORK</h2></div>
423
- <p>Число франшиз в субъекте пропорционально населению, стоимость — экономике региона. Резерв закрепляется сразу после невозвратного залога ${rub(FRANCHISE_PROGRAM.reservationDepositRub)}.</p>
423
+ <p>Число франшиз в субъекте пропорционально населению, паушальный взнос — экономике региона. Резерв закрепляется сразу после невозвратного залога ${rub(FRANCHISE_PROGRAM.reservationDepositRub)}, полная оплата паушального взноса — в течение 1 года.</p>
424
424
  </div>
425
425
  <div class="stats">
426
426
  <div class="stat"><b id="statVacant">${totalVacant}</b><span>вакантных франшиз</span></div>
427
427
  <div class="stat"><b>${visibleRegions.length}</b><span>субъектов РФ</span></div>
428
- <div class="stat"><b>${rub(FRANCHISE_PROGRAM.baseFranchiseFeeRub)}</b><span>базовая стоимость франшизы</span></div>
428
+ <div class="stat"><b>${rub(FRANCHISE_PROGRAM.baseFranchiseFeeRub)}</b><span>базовый паушальный взнос</span></div>
429
429
  <div class="stat"><b>${rub(FRANCHISE_PROGRAM.reservationDepositRub)}</b><span>залог за резерв</span></div>
430
430
  <div class="stat"><b>${FRANCHISE_PROGRAM.minDiscountPercent}→${FRANCHISE_PROGRAM.maxDiscountPercent}%</b><span>скидка партнёра от объёма</span></div>
431
431
  </div>
@@ -471,10 +471,10 @@ export function renderLandingPage({ publicKey = '', regions = null, assetVersion
471
471
  <div><div class="small muted">Вакантно франшиз</div><div class="display" style="font-size:22px;font-weight:800" id="mapRegVacant">—</div><div class="small muted" id="mapRegOccupied"></div></div>
472
472
  </div>
473
473
  <div class="kv">
474
- <div><span>Стоимость франшизы</span><b id="mapRegFee">—</b></div>
474
+ <div><span>Паушальный взнос</span><b id="mapRegFee">—</b></div>
475
475
  <div><span>Залог за резерв</span><b>${rub(FRANCHISE_PROGRAM.reservationDepositRub)}</b></div>
476
476
  <div><span>Индекс экономики</span><b id="mapRegIndex">—</b></div>
477
- <div><span>Остаток после залога</span><b id="mapRegRest">—</b></div>
477
+ <div><span>Остаток (в течение года)</span><b id="mapRegRest">—</b></div>
478
478
  </div>
479
479
  <div class="field"><label for="inpPartnerCompany">Компания</label><input type="text" id="inpPartnerCompany" value="ООО «УралТехИнтеграция»"></div>
480
480
  <div class="row2">
@@ -498,7 +498,7 @@ export function renderLandingPage({ publicKey = '', regions = null, assetVersion
498
498
  <th data-sort="district">Округ<span class="arr">↕</span></th>
499
499
  <th data-sort="populationMillions" class="num">Население<span class="arr">↕</span></th>
500
500
  <th data-sort="vacantFranchises">Вакантно<span class="arr">↕</span></th>
501
- <th data-sort="franchiseFeeRub" class="num">Франшиза<span class="arr">↕</span></th>
501
+ <th data-sort="franchiseFeeRub" class="num">Паушальный взнос<span class="arr">↕</span></th>
502
502
  <th></th>
503
503
  </tr></thead>
504
504
  <tbody id="regionsTableBody"></tbody>
package/src/store.js CHANGED
@@ -689,7 +689,7 @@ export class LicenseStore {
689
689
 
690
690
  /**
691
691
  * Зарезервировать региональную франшизу с выставлением счёта ЮKassa на невозвратный залог 100 000 ₽.
692
- * Полная стоимость франшизы (предварительно 5 000 000 ₽ × индекс региона) фиксируется в договоре резерва.
692
+ * Полный паушальный взнос (500 000 ₽ × индекс региона) фиксируется в договоре резерва, полная оплата — в течение 1 года.
693
693
  */
694
694
  reserveFranchiseSlot({
695
695
  regionCode = 'RU-SVE',
@@ -730,10 +730,12 @@ export class LicenseStore {
730
730
  franchiseFeeRub: quotaStatus.franchiseFeeRub,
731
731
  nonRefundableDepositRub: depositRub,
732
732
  remainingFranchiseFeeRub: Math.max(0, quotaStatus.franchiseFeeRub - depositRub),
733
+ fullPaymentPeriodMonths: FRANCHISE_PROGRAM.fullPaymentPeriodMonths,
734
+ fullPaymentDeadlineAt: now + 365 * 24 * 3600_000,
733
735
  status: 'reserved_pending_deposit',
734
736
  depositPaid: false,
735
737
  createdAt: now,
736
- expiresAt: now + 14 * 24 * 3600_000,
738
+ expiresAt: now + 365 * 24 * 3600_000,
737
739
  };
738
740
 
739
741
  const order = {
@@ -750,7 +752,7 @@ export class LicenseStore {
750
752
  franchiseFeeRub: quotaStatus.franchiseFeeRub,
751
753
  items: [
752
754
  {
753
- title: `Невозвратный залог за резервирование региональной франшизы ODANT-WORK (${quotaStatus.regionName}, стоимость франшизы ${quotaStatus.franchiseFeeRub.toLocaleString('ru-RU')} ₽)`,
755
+ title: `Невозвратный залог за резервирование региональной франшизы ODANT-WORK (${quotaStatus.regionName}, паушальный взнос ${quotaStatus.franchiseFeeRub.toLocaleString('ru-RU')} ₽, полная оплата в течение 1 года)`,
754
756
  quantity: 1,
755
757
  unitPriceRub: depositRub,
756
758
  totalRub: depositRub,