[{"data":1,"prerenderedAt":27},["ShallowReactive",2],{"project:crm-1c-odata":3},{"html":4,"toc":5},"\u003Cp>Сотрудник выбирает договоры в CRM и создаёт по ним счёт в 1С. Номер счёта возвращается к выбранным договорам. Позже по ним можно подготовить акт, а банковские поступления из 1С загрузить в CRM для сверки оплат.\u003C\u002Fp>\n\u003Cp>В основе этой цепочки — договор в CRM: кто заказчик, какие услуги ему оказывают и сколько они стоят. Команда связала CRM с 1С через OData, чтобы использовать эти данные при подготовке документов. Сотрудник запускает операции, интеграция собирает реквизиты, отправляет запросы и сохраняет ответы.\u003C\u002Fp>\n\u003Ch2 id=\"section-1\">Что связывает договор, счёт и акт\u003C\u002Fh2>\n\u003Cp>Для разбора возьму два договора из примера ниже: по первому нужно подготовить макет за 15 000 рублей, по второму — дважды настроить форму по 5 000. По ним нужно выставить общий счёт на 25 000 рублей и сохранить его связь с обоими договорами.\u003C\u002Fp>\n\u003Cp>CRM передаёт в 1С организацию-исполнителя, заказчика, договор контрагента в 1С и строки услуг. В ответ получает номер счёта и GUID — уникальный идентификатор документа в 1С. Номер нужен сотруднику, а GUID используется в следующих запросах: получить документ, подготовить печатное представление или связать с ним акт.\u003C\u002Fp>\n\u003Cp>Перед созданием CRM проверяет контрагента, организацию, номенклатуру и ссылку на договор контрагента в 1С. Если выбранные договоры CRM ссылаются на разные договоры в 1С, общий документ не создаётся. Если к договору CRM уже привязан счёт, форма останавливает повторное создание.\u003C\u002Fp>\n\u003Cp>Ниже разберу запросы этой цепочки на PHP: поиск контрагента, создание счёта, подготовку акта и получение поступлений. Все файлы используют одну функцию \u003Ccode>odata()\u003C\u002Fcode> и лежат в одной папке. Общий HTTP-клиент и настройка доступа приведены в разделе «Подключение к 1С для примеров». Если собираете примеры для запуска, начните с него. Запросы записи выполняйте на копии базы: они создают документы и записи справочников.\u003C\u002Fp>\n\u003Ch2 id=\"section-2\">Сначала найти контрагента и выбрать его договор в 1С\u003C\u002Fh2>\n\u003Cp>Для счёта нужен идентификатор контрагента в 1С. Интеграция сначала берёт его из записи компании в CRM. Если связи ещё нет, ищет контрагента по ИНН; при отсутствии совпадения создаёт запись по реквизитам и сохраняет её GUID. Этот идентификатор затем попадёт в счёт.\u003C\u002Fp>\n\u003Cp>В \u003Ccode>company.php\u003C\u002Fcode> поиск юридического лица уточняется по КПП. Если найдено несколько записей, программа останавливается и требует выбрать контрагента. В проекте исходный поиск выполняется по ИНН.\u003C\u002Fp>\n\u003Cpre tabindex=\"0\" aria-label=\"Блок кода: php\">\u003Ccode class=\"hljs language-php\">\u003Cspan class=\"hljs-meta\">&lt;?php\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">require\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">__DIR__\u003C\u002Fspan> . \u003Cspan class=\"hljs-string\">'\u002Fodata.php'\u003C\u002Fspan>;\n\n\u003Cspan class=\"hljs-variable\">$inn\u003C\u002Fspan> = \u003Cspan class=\"hljs-variable\">$argv\u003C\u002Fspan>[\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>] ?? \u003Cspan class=\"hljs-string\">''\u003C\u002Fspan>;\n\u003Cspan class=\"hljs-variable\">$kpp\u003C\u002Fspan> = \u003Cspan class=\"hljs-variable\">$argv\u003C\u002Fspan>[\u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>] ?? \u003Cspan class=\"hljs-string\">''\u003C\u002Fspan>;\n\u003Cspan class=\"hljs-variable\">$name\u003C\u002Fspan> = \u003Cspan class=\"hljs-variable\">$argv\u003C\u002Fspan>[\u003Cspan class=\"hljs-number\">3\u003C\u002Fspan>] ?? \u003Cspan class=\"hljs-string\">''\u003C\u002Fspan>;\n\u003Cspan class=\"hljs-variable\">$countryId\u003C\u002Fspan> = \u003Cspan class=\"hljs-variable\">$argv\u003C\u002Fspan>[\u003Cspan class=\"hljs-number\">4\u003C\u002Fspan>] ?? \u003Cspan class=\"hljs-string\">''\u003C\u002Fspan>;\n\u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!\u003Cspan class=\"hljs-title function_\">preg_match\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'\u002F^\\d{10}$\u002F'\u003C\u002Fspan>, \u003Cspan class=\"hljs-variable\">$inn\u003C\u002Fspan>) || !\u003Cspan class=\"hljs-title function_\">preg_match\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'\u002F^\\d{9}$\u002F'\u003C\u002Fspan>, \u003Cspan class=\"hljs-variable\">$kpp\u003C\u002Fspan>) || !\u003Cspan class=\"hljs-variable\">$name\u003C\u002Fspan> || !\u003Cspan class=\"hljs-variable\">$countryId\u003C\u002Fspan>) {\n    \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-built_in\">InvalidArgumentException\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'Нужны ИНН юрлица, КПП, название и GUID страны'\u003C\u002Fspan>);\n}\n\u003Cspan class=\"hljs-variable\">$result\u003C\u002Fspan> = \u003Cspan class=\"hljs-title function_\">odata\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'GET'\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">'Catalog_Контрагенты'\u003C\u002Fspan>, [\n    \u003Cspan class=\"hljs-string\">'$filter'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-string\">\"ИНН eq '\u003Cspan class=\"hljs-subst\">$inn\u003C\u002Fspan>' and КПП eq '\u003Cspan class=\"hljs-subst\">$kpp\u003C\u002Fspan>'\"\u003C\u002Fspan>,\n    \u003Cspan class=\"hljs-string\">'$select'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-string\">'Ref_Key,Description'\u003C\u002Fspan>,\n    \u003Cspan class=\"hljs-string\">'$top'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>,\n]);\n\u003Cspan class=\"hljs-variable\">$companies\u003C\u002Fspan> = \u003Cspan class=\"hljs-variable\">$result\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'value'\u003C\u002Fspan>];\n\u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (\u003Cspan class=\"hljs-title function_\">count\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable\">$companies\u003C\u002Fspan>) &gt; \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>) {\n    \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-built_in\">RuntimeException\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'Найдено несколько контрагентов: требуется выбор'\u003C\u002Fspan>);\n}\n\u003Cspan class=\"hljs-variable\">$company\u003C\u002Fspan> = \u003Cspan class=\"hljs-variable\">$companies\u003C\u002Fspan>[\u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>] ?? \u003Cspan class=\"hljs-title function_\">odata\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'POST'\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">'Catalog_Контрагенты'\u003C\u002Fspan>, [], [\n    \u003Cspan class=\"hljs-string\">'Description'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$name\u003C\u002Fspan>,\n    \u003Cspan class=\"hljs-string\">'НаименованиеПолное'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$name\u003C\u002Fspan>,\n    \u003Cspan class=\"hljs-string\">'ЮридическоеФизическоеЛицо'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-string\">'ЮридическоеЛицо'\u003C\u002Fspan>,\n    \u003Cspan class=\"hljs-string\">'СтранаРегистрации_Key'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$countryId\u003C\u002Fspan>,\n    \u003Cspan class=\"hljs-string\">'ИНН'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$inn\u003C\u002Fspan>,\n    \u003Cspan class=\"hljs-string\">'КПП'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$kpp\u003C\u002Fspan>,\n]);\n\u003Cspan class=\"hljs-keyword\">echo\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">$company\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'Ref_Key'\u003C\u002Fspan>], PHP_EOL;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Запуск: \u003Ccode>php company.php ИНН КПП 'Название компании' GUID_страны\u003C\u002Fcode>. Передайте реквизиты тестового контрагента и идентификатор страны из справочника вашей базы. Программа напечатает GUID найденной или созданной записи — его нужно сохранить для следующих обращений к этому контрагенту.\u003C\u002Fp>\n\u003Cp>Поиск и создание — два отдельных запроса. Если два процесса одновременно ищут одну компанию, оба могут получить пустой результат и затем создать по записи. При встраивании такого кода в приложение нужно учитывать параллельные запросы.\u003C\u002Fp>\n\u003Cp>После контрагента определяется его договор в 1С — запись справочника, ссылка на которую нужна в счёте и акте. Она подбирается для пары «контрагент — наша организация». Сотрудник может выбрать этот договор явно; иначе интеграция ищет подходящий в 1С, а при отсутствии создаёт основной.\u003C\u002Fp>\n\u003Cp>Список договоров контрагента запрашивается через \u003Ccode>Catalog_ДоговорыКонтрагентов\u003C\u002Fcode> с фильтром по \u003Ccode>Owner_Key\u003C\u002Fcode> и \u003Ccode>Организация_Key\u003C\u002Fcode>. В примере создания счёта ниже договор контрагента в 1С уже выбран: его GUID передаётся вместе с остальными реквизитами документа.\u003C\u002Fp>\n\u003Ch2 id=\"section-3\">Создать счёт и вернуть его номер к договорам\u003C\u002Fh2>\n\u003Cp>Теперь у нас есть реквизиты контрагента и его договора в 1С. CRM собирает из выбранных договоров наименования услуг, номенклатуру, количество и стоимость. После ответа 1С номер и GUID документа записываются у всех выбранных договоров.\u003C\u002Fp>\n\u003Cp>Чтобы повторить запрос без хранилища CRM, передадим те же виды данных в JSON. \u003Ccode>header\u003C\u002Fcode> содержит реквизиты документа, \u003Ccode>contracts\u003C\u002Fcode> — два договора CRM с собственными идентификаторами. Пример рассчитан на услуги без НДС и целое количество; деньги до отправки считаются в копейках.\u003C\u002Fp>\n\u003Cp>Сохраните \u003Ccode>invoice-input.json\u003C\u002Fcode>. GUID замените идентификаторами объектов своей базы. Для контрагента подойдёт результат \u003Ccode>company.php\u003C\u002Fcode>; для валюты документа выберите рубли.\u003C\u002Fp>\n\u003Cpre tabindex=\"0\" aria-label=\"Блок кода: json\">\u003Ccode class=\"hljs language-json\">\u003Cspan class=\"hljs-punctuation\">{\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">\"header\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">:\u003C\u002Fspan> \u003Cspan class=\"hljs-punctuation\">{\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-attr\">\"Date\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">:\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">\"2026-09-01T00:00:00\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">,\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-attr\">\"Организация_Key\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">:\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">\"11111111-1111-4111-8111-111111111111\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">,\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-attr\">\"ОрганизацияПолучатель_Key\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">:\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">\"11111111-1111-4111-8111-111111111111\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">,\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-attr\">\"Контрагент_Key\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">:\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">\"22222222-2222-4222-8222-222222222222\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">,\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-attr\">\"ДоговорКонтрагента_Key\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">:\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">\"33333333-3333-4333-8333-333333333333\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">,\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-attr\">\"ВалютаДокумента_Key\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">:\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">\"44444444-4444-4444-8444-444444444444\"\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-punctuation\">}\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">,\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">\"contracts\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">:\u003C\u002Fspan> \u003Cspan class=\"hljs-punctuation\">[\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-punctuation\">{\u003C\u002Fspan>\u003Cspan class=\"hljs-attr\">\"id\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">:\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">\"contract-1\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">,\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">\"name\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">:\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">\"Подготовка макета\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">,\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">\"quantity\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">:\u003C\u002Fspan> \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">,\u003C\u002Fspan>\n     \u003Cspan class=\"hljs-attr\">\"unit_kopecks\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">:\u003C\u002Fspan> \u003Cspan class=\"hljs-number\">1500000\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">,\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">\"item_id\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">:\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">\"55555555-5555-4555-8555-555555555555\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">}\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">,\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-punctuation\">{\u003C\u002Fspan>\u003Cspan class=\"hljs-attr\">\"id\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">:\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">\"contract-2\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">,\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">\"name\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">:\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">\"Настройка формы\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">,\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">\"quantity\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">:\u003C\u002Fspan> \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">,\u003C\u002Fspan>\n     \u003Cspan class=\"hljs-attr\">\"unit_kopecks\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">:\u003C\u002Fspan> \u003Cspan class=\"hljs-number\">500000\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">,\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">\"item_id\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">:\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">\"55555555-5555-4555-8555-555555555555\"\u003C\u002Fspan>\u003Cspan class=\"hljs-punctuation\">}\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-punctuation\">]\u003C\u002Fspan>\n\u003Cspan class=\"hljs-punctuation\">}\u003C\u002Fspan>\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Обязательные реквизиты зависят от конфигурации и настроек учёта. Если ваша база требует склад, ответственного или другие поля, добавьте их в \u003Ccode>header\u003C\u002Fcode>. Имена и типы полей доступны в \u003Ccode>$metadata\u003C\u002Fcode>, а дополнительные требования к записи определяет логика конфигурации.\u003C\u002Fp>\n\u003Cp>\u003Ccode>invoice.php\u003C\u002Fcode> переводит договоры в строки документа, считает общую сумму и отправляет счёт в 1С:\u003C\u002Fp>\n\u003Cpre tabindex=\"0\" aria-label=\"Блок кода: php\">\u003Ccode class=\"hljs language-php\">\u003Cspan class=\"hljs-meta\">&lt;?php\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">require\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">__DIR__\u003C\u002Fspan> . \u003Cspan class=\"hljs-string\">'\u002Fodata.php'\u003C\u002Fspan>;\n\n\u003Cspan class=\"hljs-variable\">$input\u003C\u002Fspan> = \u003Cspan class=\"hljs-title function_\">json_decode\u003C\u002Fspan>(\u003Cspan class=\"hljs-title function_\">file_get_contents\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable\">$argv\u003C\u002Fspan>[\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>]), \u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">512\u003C\u002Fspan>, JSON_THROW_ON_ERROR);\n\u003Cspan class=\"hljs-variable\">$rows\u003C\u002Fspan> = [];\n\u003Cspan class=\"hljs-variable\">$total\u003C\u002Fspan> = \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>;\n\u003Cspan class=\"hljs-keyword\">foreach\u003C\u002Fspan> (\u003Cspan class=\"hljs-variable\">$input\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'contracts'\u003C\u002Fspan>] \u003Cspan class=\"hljs-keyword\">as\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">$index\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$contract\u003C\u002Fspan>) {\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!\u003Cspan class=\"hljs-title function_\">is_int\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable\">$contract\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'unit_kopecks'\u003C\u002Fspan>]) || \u003Cspan class=\"hljs-variable\">$contract\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'unit_kopecks'\u003C\u002Fspan>] &lt; \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>\n        || !\u003Cspan class=\"hljs-title function_\">is_int\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable\">$contract\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'quantity'\u003C\u002Fspan>]) || \u003Cspan class=\"hljs-variable\">$contract\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'quantity'\u003C\u002Fspan>] &lt; \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>) {\n        \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-built_in\">InvalidArgumentException\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'Цена — целые копейки; количество — положительное целое'\u003C\u002Fspan>);\n    }\n    \u003Cspan class=\"hljs-variable\">$amount\u003C\u002Fspan> = \u003Cspan class=\"hljs-variable\">$contract\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'unit_kopecks'\u003C\u002Fspan>] * \u003Cspan class=\"hljs-variable\">$contract\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'quantity'\u003C\u002Fspan>];\n    \u003Cspan class=\"hljs-variable\">$total\u003C\u002Fspan> += \u003Cspan class=\"hljs-variable\">$amount\u003C\u002Fspan>;\n    \u003Cspan class=\"hljs-variable\">$rows\u003C\u002Fspan>[] = [\n        \u003Cspan class=\"hljs-string\">'LineNumber'\u003C\u002Fspan> =&gt; (\u003Cspan class=\"hljs-keyword\">string\u003C\u002Fspan>) (\u003Cspan class=\"hljs-variable\">$index\u003C\u002Fspan> + \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>),\n        \u003Cspan class=\"hljs-string\">'Номенклатура_Key'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$contract\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'item_id'\u003C\u002Fspan>],\n        \u003Cspan class=\"hljs-string\">'Содержание'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$contract\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'name'\u003C\u002Fspan>],\n        \u003Cspan class=\"hljs-string\">'Количество'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$contract\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'quantity'\u003C\u002Fspan>],\n        \u003Cspan class=\"hljs-string\">'Цена'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$contract\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'unit_kopecks'\u003C\u002Fspan>] \u002F \u003Cspan class=\"hljs-number\">100\u003C\u002Fspan>,\n        \u003Cspan class=\"hljs-string\">'Сумма'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$amount\u003C\u002Fspan> \u002F \u003Cspan class=\"hljs-number\">100\u003C\u002Fspan>,\n        \u003Cspan class=\"hljs-string\">'СтавкаНДС'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-string\">'БезНДС'\u003C\u002Fspan>,\n    ];\n}\n\u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!\u003Cspan class=\"hljs-variable\">$rows\u003C\u002Fspan>) {\n    \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-built_in\">InvalidArgumentException\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'Не выбраны договоры'\u003C\u002Fspan>);\n}\n\u003Cspan class=\"hljs-variable\">$invoice\u003C\u002Fspan> = \u003Cspan class=\"hljs-title function_\">odata\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'POST'\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">'Document_СчетНаОплатуПокупателю'\u003C\u002Fspan>, [], \u003Cspan class=\"hljs-title function_\">array_replace\u003C\u002Fspan>(\n    \u003Cspan class=\"hljs-variable\">$input\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'header'\u003C\u002Fspan>],\n    [\u003Cspan class=\"hljs-string\">'Posted'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">'ДокументБезНДС'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>,\n     \u003Cspan class=\"hljs-string\">'СуммаДокумента'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$total\u003C\u002Fspan> \u002F \u003Cspan class=\"hljs-number\">100\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">'Товары'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$rows\u003C\u002Fspan>]\n));\n\u003Cspan class=\"hljs-keyword\">echo\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">json_encode\u003C\u002Fspan>([\n    \u003Cspan class=\"hljs-string\">'invoice_id'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$invoice\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'Ref_Key'\u003C\u002Fspan>],\n    \u003Cspan class=\"hljs-string\">'number'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$invoice\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'Number'\u003C\u002Fspan>],\n    \u003Cspan class=\"hljs-string\">'contract_ids'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-title function_\">array_column\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable\">$input\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'contracts'\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">'id'\u003C\u002Fspan>),\n], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR), PHP_EOL;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Запустите \u003Ccode>php invoice.php invoice-input.json\u003C\u002Fcode>. Сумма составит 25 000 рублей: 15 000 за макет и 10 000 за две настройки формы. Программа вернёт \u003Ccode>invoice_id\u003C\u002Fcode>, \u003Ccode>number\u003C\u002Fcode> и \u003Ccode>contract_ids\u003C\u002Fcode> со значениями \u003Ccode>contract-1\u003C\u002Fcode> и \u003Ccode>contract-2\u003C\u002Fcode>. По этому ответу можно записать номер и идентификатор счёта к обоим договорам — именно такую связь сохраняет интеграция в CRM.\u003C\u002Fp>\n\u003Cp>Счёт создаётся непроведённым (\u003Ccode>Posted: false\u003C\u002Fcode>), чтобы реквизиты и услуги можно было проверить до проведения. Повторный запуск создаст ещё один документ. Перед включением примера в приложение нужна проверка уже сохранённого счёта, как в форме проекта.\u003C\u002Fp>\n\u003Cp>При тайм-ауте 1С могла уже создать счёт, хотя CRM ещё не получила его GUID. Перед повторной отправкой нужно проверить результат первого запроса.\u003C\u002Fp>\n\u003Ch2 id=\"section-4\">Подготовить акт по связанным договорам\u003C\u002Fh2>\n\u003Cp>Для акта CRM находит счёт выбранного договора, затем собирает все договоры CRM, связанные с этим счётом. Здесь и используется связь, сохранённая на предыдущем шаге. Договор контрагента в 1С берётся из явного выбора сотрудника, затем из счёта; если его там нет, подбирается по контрагенту и организации. После создания номер и GUID акта записываются у договоров, затем предусмотрен вызов проведения.\u003C\u002Fp>\n\u003Cp>В \u003Ccode>act.php\u003C\u002Fcode> источником строк служит счёт в 1С. Такой пример можно запустить без таблиц CRM: получить счёт по GUID, перенести услуги и реквизиты в документ реализации, сохранить ссылку на счёт в документе и его строках. Он рассчитан на счёт из предыдущего примера — только услуги без НДС.\u003C\u002Fp>\n\u003Cpre tabindex=\"0\" aria-label=\"Блок кода: php\">\u003Ccode class=\"hljs language-php\">\u003Cspan class=\"hljs-meta\">&lt;?php\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">require\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">__DIR__\u003C\u002Fspan> . \u003Cspan class=\"hljs-string\">'\u002Fodata.php'\u003C\u002Fspan>;\n\n\u003Cspan class=\"hljs-variable\">$invoiceId\u003C\u002Fspan> = \u003Cspan class=\"hljs-variable\">$argv\u003C\u002Fspan>[\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>] ?? \u003Cspan class=\"hljs-string\">''\u003C\u002Fspan>;\n\u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!\u003Cspan class=\"hljs-title function_\">preg_match\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'\u002F^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$\u002Fi'\u003C\u002Fspan>, \u003Cspan class=\"hljs-variable\">$invoiceId\u003C\u002Fspan>)) {\n    \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-built_in\">InvalidArgumentException\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'Нужен GUID счёта'\u003C\u002Fspan>);\n}\n\u003Cspan class=\"hljs-variable\">$invoice\u003C\u002Fspan> = \u003Cspan class=\"hljs-title function_\">odata\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'GET'\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">\"Document_СчетНаОплатуПокупателю(guid'\u003Cspan class=\"hljs-subst\">$invoiceId\u003C\u002Fspan>')\"\u003C\u002Fspan>);\n\u003Cspan class=\"hljs-variable\">$services\u003C\u002Fspan> = [];\n\u003Cspan class=\"hljs-keyword\">foreach\u003C\u002Fspan> (\u003Cspan class=\"hljs-variable\">$invoice\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'Товары'\u003C\u002Fspan>] \u003Cspan class=\"hljs-keyword\">as\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">$index\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$row\u003C\u002Fspan>) {\n    \u003Cspan class=\"hljs-variable\">$services\u003C\u002Fspan>[] = [\n        \u003Cspan class=\"hljs-string\">'LineNumber'\u003C\u002Fspan> =&gt; (\u003Cspan class=\"hljs-keyword\">string\u003C\u002Fspan>) (\u003Cspan class=\"hljs-variable\">$index\u003C\u002Fspan> + \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>),\n        \u003Cspan class=\"hljs-string\">'Номенклатура_Key'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$row\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'Номенклатура_Key'\u003C\u002Fspan>],\n        \u003Cspan class=\"hljs-string\">'Содержание'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$row\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'Содержание'\u003C\u002Fspan>],\n        \u003Cspan class=\"hljs-string\">'Количество'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$row\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'Количество'\u003C\u002Fspan>],\n        \u003Cspan class=\"hljs-string\">'Цена'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$row\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'Цена'\u003C\u002Fspan>],\n        \u003Cspan class=\"hljs-string\">'Сумма'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$row\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'Сумма'\u003C\u002Fspan>],\n        \u003Cspan class=\"hljs-string\">'СтавкаНДС'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-string\">'БезНДС'\u003C\u002Fspan>,\n        \u003Cspan class=\"hljs-string\">'СчетНаОплатуПокупателю_Key'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$invoiceId\u003C\u002Fspan>,\n    ];\n}\n\u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!\u003Cspan class=\"hljs-variable\">$services\u003C\u002Fspan>) {\n    \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-built_in\">RuntimeException\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'В счёте нет строк услуг'\u003C\u002Fspan>);\n}\n\u003Cspan class=\"hljs-variable\">$act\u003C\u002Fspan> = \u003Cspan class=\"hljs-title function_\">odata\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'POST'\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">'Document_РеализацияТоваровУслуг'\u003C\u002Fspan>, [], [\n    \u003Cspan class=\"hljs-string\">'Date'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-title function_\">date\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'Y-m-d\\TH:i:s'\u003C\u002Fspan>),\n    \u003Cspan class=\"hljs-string\">'Posted'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>,\n    \u003Cspan class=\"hljs-string\">'ВидОперации'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-string\">'Услуги'\u003C\u002Fspan>,\n    \u003Cspan class=\"hljs-string\">'Организация_Key'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$invoice\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'Организация_Key'\u003C\u002Fspan>],\n    \u003Cspan class=\"hljs-string\">'Контрагент_Key'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$invoice\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'Контрагент_Key'\u003C\u002Fspan>],\n    \u003Cspan class=\"hljs-string\">'ДоговорКонтрагента_Key'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$invoice\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'ДоговорКонтрагента_Key'\u003C\u002Fspan>],\n    \u003Cspan class=\"hljs-string\">'ВалютаДокумента_Key'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$invoice\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'ВалютаДокумента_Key'\u003C\u002Fspan>],\n    \u003Cspan class=\"hljs-string\">'ДокументБезНДС'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>,\n    \u003Cspan class=\"hljs-string\">'СчетНаОплатуПокупателю_Key'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$invoiceId\u003C\u002Fspan>,\n    \u003Cspan class=\"hljs-string\">'СуммаДокумента'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$invoice\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'СуммаДокумента'\u003C\u002Fspan>],\n    \u003Cspan class=\"hljs-string\">'Услуги'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$services\u003C\u002Fspan>,\n]);\n\u003Cspan class=\"hljs-keyword\">echo\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">$act\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'Ref_Key'\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">' '\u003C\u002Fspan>, \u003Cspan class=\"hljs-variable\">$act\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'Number'\u003C\u002Fspan>], PHP_EOL;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Запуск: \u003Ccode>php act.php GUID_счёта\u003C\u002Fcode>. На выходе — GUID и номер акта. Контрагент, организация, договор контрагента в 1С, валюта и строки услуг взяты из счёта, поэтому повторно вводить их не требуется.\u003C\u002Fp>\n\u003Cp>В примере акт остаётся непроведённым. Для проведения могут понадобиться счета учёта и аналитика строк услуг; их состав зависит от конфигурации. В CRM проекта эти реквизиты задаются при подготовке документа. После их заполнения проведение вызывается отдельным \u003Ccode>POST\u003C\u002Fcode> к \u003Ccode>Document_РеализацияТоваровУслуг(guid'GUID_акта')\u002FPost()\u003C\u002Fcode>. Успешность этой операции проверяется по её ответу.\u003C\u002Fp>\n\u003Cp>Когда нужны печатные представления счёта и акта, CRM запрашивает документ из 1С и подставляет данные вместе с реквизитами сторон в шаблон. Состав услуг берётся из документа.\u003C\u002Fp>\n\u003Ch2 id=\"section-5\">Загрузить банковские поступления для сверки\u003C\u002Fh2>\n\u003Cp>После подготовки документов остаётся проверить оплату. Для этого данные идут в обратную сторону: сотрудник выбирает в CRM месяц и загружает из 1С поступления на расчётный счёт. В форме доступны дата, сумма, назначение платежа и реквизиты плательщика; сотрудник выбирает поступление для дальнейшей сверки с договором CRM.\u003C\u002Fp>\n\u003Cp>\u003Ccode>payments.php\u003C\u002Fcode> получает проведённые поступления за месяц и возвращает JSON. В выборку входят идентификатор, дата, сумма и назначение платежа:\u003C\u002Fp>\n\u003Cpre tabindex=\"0\" aria-label=\"Блок кода: php\">\u003Ccode class=\"hljs language-php\">\u003Cspan class=\"hljs-meta\">&lt;?php\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">require\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">__DIR__\u003C\u002Fspan> . \u003Cspan class=\"hljs-string\">'\u002Fodata.php'\u003C\u002Fspan>;\n\n\u003Cspan class=\"hljs-variable\">$month\u003C\u002Fspan> = \u003Cspan class=\"hljs-variable\">$argv\u003C\u002Fspan>[\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>] ?? \u003Cspan class=\"hljs-string\">''\u003C\u002Fspan>;\n\u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!\u003Cspan class=\"hljs-title function_\">preg_match\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'\u002F^\\d{4}-(0[1-9]|1[0-2])$\u002F'\u003C\u002Fspan>, \u003Cspan class=\"hljs-variable\">$month\u003C\u002Fspan>)) {\n    \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-built_in\">InvalidArgumentException\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'Месяц должен иметь формат YYYY-MM'\u003C\u002Fspan>);\n}\n\u003Cspan class=\"hljs-variable\">$start\u003C\u002Fspan> = \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">DateTimeImmutable\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable\">$month\u003C\u002Fspan> . \u003Cspan class=\"hljs-string\">'-01T00:00:00'\u003C\u002Fspan>);\n\u003Cspan class=\"hljs-variable\">$end\u003C\u002Fspan> = \u003Cspan class=\"hljs-variable\">$start\u003C\u002Fspan>-&gt;\u003Cspan class=\"hljs-title function_\">modify\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'+1 month'\u003C\u002Fspan>);\n\u003Cspan class=\"hljs-variable\">$from\u003C\u002Fspan> = \u003Cspan class=\"hljs-variable\">$start\u003C\u002Fspan>-&gt;\u003Cspan class=\"hljs-title function_\">format\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'Y-m-d\\TH:i:s'\u003C\u002Fspan>);\n\u003Cspan class=\"hljs-variable\">$to\u003C\u002Fspan> = \u003Cspan class=\"hljs-variable\">$end\u003C\u002Fspan>-&gt;\u003Cspan class=\"hljs-title function_\">format\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'Y-m-d\\TH:i:s'\u003C\u002Fspan>);\n\u003Cspan class=\"hljs-variable\">$payments\u003C\u002Fspan> = [];\n\u003Cspan class=\"hljs-keyword\">for\u003C\u002Fspan> (\u003Cspan class=\"hljs-variable\">$skip\u003C\u002Fspan> = \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>; ; \u003Cspan class=\"hljs-variable\">$skip\u003C\u002Fspan> += \u003Cspan class=\"hljs-title function_\">count\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable\">$page\u003C\u002Fspan>)) {\n    \u003Cspan class=\"hljs-variable\">$page\u003C\u002Fspan> = \u003Cspan class=\"hljs-title function_\">odata\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'GET'\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">'Document_ПоступлениеНаРасчетныйСчет'\u003C\u002Fspan>, [\n        \u003Cspan class=\"hljs-string\">'$filter'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-string\">\"Date ge datetime'\u003Cspan class=\"hljs-subst\">$from\u003C\u002Fspan>' and Date lt datetime'\u003Cspan class=\"hljs-subst\">$to\u003C\u002Fspan>'\"\u003C\u002Fspan>\n            . \u003Cspan class=\"hljs-string\">' and Posted eq true and DeletionMark eq false'\u003C\u002Fspan>,\n        \u003Cspan class=\"hljs-string\">'$select'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-string\">'Ref_Key,Date,СуммаДокумента,НазначениеПлатежа'\u003C\u002Fspan>,\n        \u003Cspan class=\"hljs-string\">'$orderby'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-string\">'Date asc,Ref_Key asc'\u003C\u002Fspan>,\n        \u003Cspan class=\"hljs-string\">'$top'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-number\">100\u003C\u002Fspan>,\n        \u003Cspan class=\"hljs-string\">'$skip'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-variable\">$skip\u003C\u002Fspan>,\n    ])[\u003Cspan class=\"hljs-string\">'value'\u003C\u002Fspan>];\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!\u003Cspan class=\"hljs-variable\">$page\u003C\u002Fspan>) {\n        \u003Cspan class=\"hljs-keyword\">break\u003C\u002Fspan>;\n    }\n    \u003Cspan class=\"hljs-keyword\">foreach\u003C\u002Fspan> (\u003Cspan class=\"hljs-variable\">$page\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">as\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">$payment\u003C\u002Fspan>) {\n        \u003Cspan class=\"hljs-variable\">$payments\u003C\u002Fspan>[\u003Cspan class=\"hljs-variable\">$payment\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">'Ref_Key'\u003C\u002Fspan>]] = \u003Cspan class=\"hljs-variable\">$payment\u003C\u002Fspan>;\n    }\n}\n\u003Cspan class=\"hljs-keyword\">echo\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">json_encode\u003C\u002Fspan>(\u003Cspan class=\"hljs-title function_\">array_values\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable\">$payments\u003C\u002Fspan>),\n    JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR), PHP_EOL;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Запуск: \u003Ccode>php payments.php 2026-09\u003C\u002Fcode>. Начало периода включено, начало следующего месяца исключено условием \u003Ccode>Date lt\u003C\u002Fcode>. Запрос охватывает сентябрь целиком, без подбора последнего дня и времени месяца.\u003C\u002Fp>\n\u003Cp>В исходной форме CRM запрашивается до 200 поступлений за выбранный месяц. В примере выгрузка расширена: запросы идут страницами по 100 записей до пустого ответа, а фильтры оставляют проведённые документы без пометки удаления. Эти дополнения относятся к примеру.\u003C\u002Fp>\n\u003Cp>Сортировка по дате и GUID задаёт однозначный порядок записей. Если во время обхода через \u003Ccode>$skip\u003C\u002Fcode> документы меняются, состав страниц может измениться. Для регулярной синхронизации потребуется повторная сверка по идентификаторам.\u003C\u002Fp>\n\u003Ch2 id=\"section-6\">Подключение к 1С для примеров\u003C\u002Fh2>\n\u003Cp>Примерам нужны PHP 8 с расширением cURL, адрес публикации OData и пользователь с правами на соответствующие справочники и документы. Имена объектов и полей сверьте запросом \u003Ccode>GET\u003C\u002Fcode> к \u003Ccode>$metadata\u003C\u002Fcode> вашей публикации. Учётные данные передаются через переменные окружения; в Bash пароль можно ввести без отображения:\u003C\u002Fp>\n\u003Cpre tabindex=\"0\" aria-label=\"Блок кода: bash\">\u003Ccode class=\"hljs language-bash\">\u003Cspan class=\"hljs-built_in\">export\u003C\u002Fspan> ODATA_URL=\u003Cspan class=\"hljs-string\">'https:\u002F\u002Fexample.com\u002Fbase\u002Fodata\u002Fstandard.odata\u002F'\u003C\u002Fspan>\n\u003Cspan class=\"hljs-built_in\">read\u003C\u002Fspan> -r -p \u003Cspan class=\"hljs-string\">'Логин 1С: '\u003C\u002Fspan> ODATA_USER\n\u003Cspan class=\"hljs-built_in\">read\u003C\u002Fspan> -r -s -p \u003Cspan class=\"hljs-string\">'Пароль: '\u003C\u002Fspan> ODATA_PASSWORD\n\u003Cspan class=\"hljs-built_in\">export\u003C\u002Fspan> ODATA_USER ODATA_PASSWORD\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Замените адрес своим. Функцию ниже сохраните в \u003Ccode>odata.php\u003C\u002Fcode> рядом с четырьмя файлами сценариев. Она собирает адрес и параметры, отправляет JSON и возвращает разобранный ответ 1С.\u003C\u002Fp>\n\u003Cpre tabindex=\"0\" aria-label=\"Блок кода: php\">\u003Ccode class=\"hljs language-php\">\u003Cspan class=\"hljs-meta\">&lt;?php\u003C\u002Fspan>\n\u003Cspan class=\"hljs-function\">\u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title\">odata\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-keyword\">string\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">$method\u003C\u002Fspan>, \u003Cspan class=\"hljs-keyword\">string\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">$resource\u003C\u002Fspan>, \u003Cspan class=\"hljs-keyword\">array\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">$query\u003C\u002Fspan> = [], ?\u003Cspan class=\"hljs-keyword\">array\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">$body\u003C\u002Fspan> = \u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>\u003C\u002Fspan>): \u003Cspan class=\"hljs-title\">array\u003C\u002Fspan>\n\u003C\u002Fspan>{\n    \u003Cspan class=\"hljs-variable\">$base\u003C\u002Fspan> = \u003Cspan class=\"hljs-title function_\">getenv\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'ODATA_URL'\u003C\u002Fspan>);\n    \u003Cspan class=\"hljs-variable\">$user\u003C\u002Fspan> = \u003Cspan class=\"hljs-title function_\">getenv\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'ODATA_USER'\u003C\u002Fspan>);\n    \u003Cspan class=\"hljs-variable\">$password\u003C\u002Fspan> = \u003Cspan class=\"hljs-title function_\">getenv\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'ODATA_PASSWORD'\u003C\u002Fspan>);\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!\u003Cspan class=\"hljs-variable\">$base\u003C\u002Fspan> || !\u003Cspan class=\"hljs-variable\">$user\u003C\u002Fspan> || \u003Cspan class=\"hljs-variable\">$password\u003C\u002Fspan> === \u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>) {\n        \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-built_in\">RuntimeException\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'Задайте ODATA_URL, ODATA_USER и ODATA_PASSWORD'\u003C\u002Fspan>);\n    }\n    \u003Cspan class=\"hljs-variable\">$path\u003C\u002Fspan> = \u003Cspan class=\"hljs-title function_\">implode\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'\u002F'\u003C\u002Fspan>, \u003Cspan class=\"hljs-title function_\">array_map\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'rawurlencode'\u003C\u002Fspan>, \u003Cspan class=\"hljs-title function_\">explode\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'\u002F'\u003C\u002Fspan>, \u003Cspan class=\"hljs-variable\">$resource\u003C\u002Fspan>)));\n    \u003Cspan class=\"hljs-variable\">$url\u003C\u002Fspan> = \u003Cspan class=\"hljs-title function_\">rtrim\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable\">$base\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">'\u002F'\u003C\u002Fspan>) . \u003Cspan class=\"hljs-string\">'\u002F'\u003C\u002Fspan> . \u003Cspan class=\"hljs-variable\">$path\u003C\u002Fspan> . \u003Cspan class=\"hljs-string\">'?'\u003C\u002Fspan> . \u003Cspan class=\"hljs-title function_\">http_build_query\u003C\u002Fspan>(\n        \u003Cspan class=\"hljs-variable\">$query\u003C\u002Fspan> + [\u003Cspan class=\"hljs-string\">'$format'\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-string\">'json'\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">''\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">'&amp;'\u003C\u002Fspan>, PHP_QUERY_RFC3986\n    );\n    \u003Cspan class=\"hljs-variable\">$headers\u003C\u002Fspan> = [\u003Cspan class=\"hljs-string\">'Accept: application\u002Fjson'\u003C\u002Fspan>];\n    \u003Cspan class=\"hljs-variable\">$options\u003C\u002Fspan> = [\n        CURLOPT_RETURNTRANSFER =&gt; \u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>,\n        CURLOPT_CUSTOMREQUEST =&gt; \u003Cspan class=\"hljs-variable\">$method\u003C\u002Fspan>,\n        CURLOPT_HTTPAUTH =&gt; CURLAUTH_BASIC,\n        CURLOPT_USERPWD =&gt; \u003Cspan class=\"hljs-variable\">$user\u003C\u002Fspan> . \u003Cspan class=\"hljs-string\">':'\u003C\u002Fspan> . \u003Cspan class=\"hljs-variable\">$password\u003C\u002Fspan>,\n        CURLOPT_CONNECTTIMEOUT =&gt; \u003Cspan class=\"hljs-number\">10\u003C\u002Fspan>,\n        CURLOPT_TIMEOUT =&gt; \u003Cspan class=\"hljs-number\">30\u003C\u002Fspan>,\n    ];\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (\u003Cspan class=\"hljs-variable\">$body\u003C\u002Fspan> !== \u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>) {\n        \u003Cspan class=\"hljs-variable\">$options\u003C\u002Fspan>[CURLOPT_POSTFIELDS] = \u003Cspan class=\"hljs-title function_\">json_encode\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable\">$body\u003C\u002Fspan>, JSON_THROW_ON_ERROR);\n        \u003Cspan class=\"hljs-variable\">$headers\u003C\u002Fspan>[] = \u003Cspan class=\"hljs-string\">'Content-Type: application\u002Fjson; charset=utf-8'\u003C\u002Fspan>;\n    }\n    \u003Cspan class=\"hljs-variable\">$options\u003C\u002Fspan>[CURLOPT_HTTPHEADER] = \u003Cspan class=\"hljs-variable\">$headers\u003C\u002Fspan>;\n    \u003Cspan class=\"hljs-variable\">$curl\u003C\u002Fspan> = \u003Cspan class=\"hljs-title function_\">curl_init\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable\">$url\u003C\u002Fspan>);\n    \u003Cspan class=\"hljs-title function_\">curl_setopt_array\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable\">$curl\u003C\u002Fspan>, \u003Cspan class=\"hljs-variable\">$options\u003C\u002Fspan>);\n    \u003Cspan class=\"hljs-variable\">$response\u003C\u002Fspan> = \u003Cspan class=\"hljs-title function_\">curl_exec\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable\">$curl\u003C\u002Fspan>);\n    \u003Cspan class=\"hljs-variable\">$status\u003C\u002Fspan> = \u003Cspan class=\"hljs-title function_\">curl_getinfo\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable\">$curl\u003C\u002Fspan>, CURLINFO_RESPONSE_CODE);\n    \u003Cspan class=\"hljs-variable\">$error\u003C\u002Fspan> = \u003Cspan class=\"hljs-title function_\">curl_error\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable\">$curl\u003C\u002Fspan>);\n    \u003Cspan class=\"hljs-title function_\">curl_close\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable\">$curl\u003C\u002Fspan>);\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (\u003Cspan class=\"hljs-variable\">$response\u003C\u002Fspan> === \u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>) {\n        \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-built_in\">RuntimeException\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'Ошибка связи: '\u003C\u002Fspan> . \u003Cspan class=\"hljs-variable\">$error\u003C\u002Fspan>);\n    }\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (\u003Cspan class=\"hljs-variable\">$status\u003C\u002Fspan> &lt; \u003Cspan class=\"hljs-number\">200\u003C\u002Fspan> || \u003Cspan class=\"hljs-variable\">$status\u003C\u002Fspan> &gt;= \u003Cspan class=\"hljs-number\">300\u003C\u002Fspan>) {\n        \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-built_in\">RuntimeException\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">'1С вернула HTTP '\u003C\u002Fspan> . \u003Cspan class=\"hljs-variable\">$status\u003C\u002Fspan> . \u003Cspan class=\"hljs-string\">': '\u003C\u002Fspan> . \u003Cspan class=\"hljs-variable\">$response\u003C\u002Fspan>);\n    }\n    \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> \u003Cspan class=\"hljs-variable\">$response\u003C\u002Fspan> === \u003Cspan class=\"hljs-string\">''\u003C\u002Fspan> ? [] : \u003Cspan class=\"hljs-title function_\">json_decode\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable\">$response\u003C\u002Fspan>, \u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">512\u003C\u002Fspan>, JSON_THROW_ON_ERROR);\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>При ошибке HTTP функция прерывает выполнение. После тайм-аута она также возвращает ошибку; проверка того, успела ли 1С создать объект, остаётся вызывающему коду. Автоматического повторения запроса здесь нет.\u003C\u002Fp>\n\u003Ch2 id=\"section-7\">Что остаётся сотруднику\u003C\u002Fh2>\n\u003Cp>Создание документов запускает сотрудник. Он же подтверждает связь банковского поступления с договором CRM. Интеграция берёт на себя подготовку данных и обмен с 1С, а результат сохраняет в карточках договоров CRM: можно открыть связанный документ, подготовить акт или печатное представление без повторного набора услуг.\u003C\u002Fp>\n\u003Cp>Примеры написаны специально для кейса. Они показывают операции OData и обработку ответа; отличия от реализации CRM описаны рядом с соответствующими сценариями.\u003C\u002Fp>\n",[6,9,12,15,18,21,24],{"id":7,"title":8},"section-1","Что связывает договор, счёт и акт",{"id":10,"title":11},"section-2","Сначала найти контрагента и выбрать его договор в 1С",{"id":13,"title":14},"section-3","Создать счёт и вернуть его номер к договорам",{"id":16,"title":17},"section-4","Подготовить акт по связанным договорам",{"id":19,"title":20},"section-5","Загрузить банковские поступления для сверки",{"id":22,"title":23},"section-6","Подключение к 1С для примеров",{"id":25,"title":26},"section-7","Что остаётся сотруднику",1790862055466]