feat(parsers): добавлен парсер zakupki.gov.ru с SOAP API интеграцией
Реализована полная интеграция с ЕИС Закупки через SOAP API (FTP доступ закрыт с 01.01.2025). Добавлено: - ZakupkiClient с поддержкой SOAP методов getDocsByOrgRegionRequest и getDocsByReestrNumberRequest - Модель ProcurementRecord (18 полей, 3 индекса) - ProcurementService и ParserLoadLogService для бизнес-логики - Celery задачи parse_procurements и sync_procurements - Админка с цветовой индикацией статусов и фильтрами - 71 тест (unit + E2E с RUN_E2E_TESTS=1) Требования: токен SOAP API через Госуслуги 🤖 Generated with [Qoder][https://qoder.com]
This commit is contained in:
67
src/apps/parsers/migrations/0006_add_procurement_model.py
Normal file
67
src/apps/parsers/migrations/0006_add_procurement_model.py
Normal file
@@ -0,0 +1,67 @@
|
||||
# Generated by Django 3.2.25 on 2026-01-27 11:56
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('parsers', '0005_add_inspection_fz248_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ProcurementRecord',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, db_index=True, help_text='Дата и время создания записи', verbose_name='создано')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, help_text='Дата и время последнего обновления', verbose_name='обновлено')),
|
||||
('load_batch', models.PositiveIntegerField(db_index=True, help_text='Идентификатор пакета загрузки', verbose_name='ID пакета загрузки')),
|
||||
('purchase_number', models.CharField(db_index=True, help_text='Реестровый номер закупки', max_length=100, verbose_name='реестровый номер')),
|
||||
('purchase_name', models.TextField(help_text='Наименование закупки', verbose_name='наименование закупки')),
|
||||
('customer_inn', models.CharField(db_index=True, help_text='ИНН заказчика', max_length=20, verbose_name='ИНН заказчика')),
|
||||
('customer_kpp', models.CharField(blank=True, help_text='КПП заказчика', max_length=20, verbose_name='КПП заказчика')),
|
||||
('customer_ogrn', models.CharField(blank=True, db_index=True, help_text='ОГРН заказчика', max_length=20, verbose_name='ОГРН заказчика')),
|
||||
('customer_name', models.TextField(help_text='Наименование заказчика', verbose_name='наименование заказчика')),
|
||||
('max_price', models.CharField(blank=True, help_text='Начальная (максимальная) цена контракта', max_length=50, verbose_name='НМЦ')),
|
||||
('currency_code', models.CharField(default='RUB', help_text='Код валюты', max_length=10, verbose_name='валюта')),
|
||||
('placement_method', models.CharField(blank=True, help_text='Способ определения поставщика', max_length=255, verbose_name='способ определения')),
|
||||
('publish_date', models.CharField(blank=True, help_text='Дата публикации извещения', max_length=30, verbose_name='дата публикации')),
|
||||
('end_date', models.CharField(blank=True, help_text='Дата окончания подачи заявок', max_length=30, verbose_name='дата окончания')),
|
||||
('status', models.CharField(blank=True, help_text='Статус закупки', max_length=100, verbose_name='статус')),
|
||||
('law_type', models.CharField(blank=True, db_index=True, help_text='Тип закона (44-ФЗ, 223-ФЗ)', max_length=20, verbose_name='тип закона')),
|
||||
('purchase_object_info', models.TextField(blank=True, help_text='Информация об объекте закупки', verbose_name='объект закупки')),
|
||||
('href', models.URLField(blank=True, help_text='Ссылка на страницу закупки', max_length=500, verbose_name='ссылка')),
|
||||
('region_code', models.CharField(blank=True, db_index=True, help_text='Код региона', max_length=10, verbose_name='код региона')),
|
||||
('data_year', models.PositiveSmallIntegerField(blank=True, db_index=True, help_text='Год, за который загружены данные', null=True, verbose_name='год данных')),
|
||||
('data_month', models.PositiveSmallIntegerField(blank=True, db_index=True, help_text='Месяц, за который загружены данные', null=True, verbose_name='месяц данных')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'закупка',
|
||||
'verbose_name_plural': 'закупки',
|
||||
'db_table': 'parsers_procurement',
|
||||
'ordering': ['-created_at'],
|
||||
},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='parserloadlog',
|
||||
name='source',
|
||||
field=models.CharField(choices=[('industrial', 'Промышленное производство'), ('manufactures', 'Реестр производителей'), ('inspections', 'Единый реестр проверок'), ('procurements', 'Государственные закупки')], db_index=True, help_text='Источник данных', max_length=50, verbose_name='источник'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='procurementrecord',
|
||||
index=models.Index(fields=['customer_inn', 'purchase_number'], name='parsers_pro_custome_8e0271_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='procurementrecord',
|
||||
index=models.Index(fields=['load_batch', 'customer_inn'], name='parsers_pro_load_ba_ca8e7f_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='procurementrecord',
|
||||
index=models.Index(fields=['law_type', 'data_year', 'data_month'], name='parsers_pro_law_typ_5a53c9_idx'),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='procurementrecord',
|
||||
constraint=models.UniqueConstraint(fields=('purchase_number',), name='unique_procurement_purchase_number'),
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user