Перейти к содержимому

Оплаты и отгрузки заказа - объекты, признаки, частичные операции

Разбираемся, как заказ хранит оплаты и отгрузки, почему сумма оплаты и признак оплаченности живут отдельно и что снимает резерв товара.

Решение

Смотрим состав заказа по операциям:

$order = \Bitrix\Sale\Order::load($orderId);
foreach ($order->getPaymentCollection() as $payment) {
printf("оплата %s: %8.2f система=%s оплачена=%s\n",
$payment->getId(), $payment->getSum(),
$payment->getPaymentSystemId(), $payment->isPaid() ? 'да' : 'нет');
}
foreach ($order->getShipmentCollection() as $shipment) {
if ($shipment->isSystem()) { continue; } // служебная отгрузка в расчёт не идёт
printf("отгрузка %s: отгружена=%s\n", $shipment->getId(), $shipment->isShipped() ? 'да' : 'нет');
}

Оплаты и отгрузки - отдельные наборы внутри заказа, а не его поля. Заказ может иметь две оплаты и три отгрузки, и общий признак складывается из состояния этих объектов, а не задаётся отдельно.

Отмечаем оплату из кода:

$payment = $order->getPaymentCollection()->getItemById($paymentId);
$payment->setPaid('Y'); // отметка ставится объекту оплаты
$result = $order->save(); // признак заказа пересчитается при сохранении
echo $result->isSuccess() ? "готово\n" : implode("\n", $result->getErrorMessages());

Отметка ставится объекту оплаты, а не полю заказа. Прямая запись признака в таблицу заказа оставляет оплату неоплаченной, и учётная система при следующем обмене возвращает всё обратно.

Работаем с отгрузкой и резервом:

$shipment = $order->getShipmentCollection()->createItem(
\Bitrix\Sale\Delivery\Services\Manager::getObjectById($deliveryId));
$shipment->setField('ALLOW_DELIVERY', 'Y'); // разрешение отгрузки
$shipment->setField('DEDUCTED', 'Y'); // товар списан со склада
$order->save();

Резерв товара снимается отгрузкой заказа, а не его оплатой. Оплаченный, но не отгруженный заказ продолжает держать товар в резерве, и склад показывает меньше свободного количества, чем есть на самом деле.

Проверяем итоговые суммы:

printf("сумма=%8.2f оплачено=%8.2f осталось=%8.2f\n",
$order->getPrice(), $order->getSumPaid(), $order->getPrice() - $order->getSumPaid());
// частичная оплата: сумма оплат меньше стоимости заказа
var_dump($order->isPaid(), $order->isShipped()); // общие признаки заказа
// признак оплаты ставит обработчик платёжной системы, а не менеджер вручную
// у отгрузки свои признаки: собрана, отгружена и разрешена к отгрузке

Сумму всех оплат со стоимостью заказа платформа сравнивает сама. Частичная оплата оставляет заказ неоплаченным целиком, хотя деньги по одной из оплат уже получены, и это нормальное состояние, а не ошибка.

Смена стоимости заказа после оплаты саму эту оплату не отменяет. Разницу закрывают новой оплатой или возвратом, а не правкой суммы, иначе расхождение всплывает при сверке с учётной системой.

Отгрузку и оплату стоит разводить и в рабочем интерфейсе менеджера. Смешанные в одну кнопку операции приводят к заказам, отгруженным без денег, и деньгам без отгрузки, а сверка с учётной системой такие расхождения показывает не сразу.

Событие смены признака оплаты остаётся главной точкой для подключения своей логики. Отправка чека, запуск сборки и уведомление склада вешаются на него, а не на статус заказа, который менеджер может поставить руками.

Типичные проблемы

Заказ не считается оплаченным после оплаты.

Признак поставлен самому заказу напрямую, а не объекту его оплаты. Общий признак заказа собирается платформой из состояния всех его оплат.

Товар висит в резерве у оплаченного заказа.

Резерв товара снимается именно отгрузкой заказа, а не самим фактом оплаты. Пока отгрузки нет, склад продолжает держать это количество занятым под данный заказ.

Заказ оплачен частично и выглядит неоплаченным.

Сумма всех оплат заказа меньше его полной стоимости. Признак оплаты выставляется заказу только при полном покрытии всей его суммы.

Правки оплат не сохраняются.

Заказ не сохранён после изменения самого объекта оплаты. Изменения этих наборов записываются в базу только при сохранении самого объекта заказа.

После смены суммы заказа сверка не сходится.

Стоимость заказа изменена задним числом, а его оплаты остались прежними. Разницу закрывают новой оплатой или возвратом денег, а не правкой суммы заказа.

Частые вопросы

Можно ли завести две оплаты на один заказ?

Да, коллекция оплат для этого и сделана. Так оформляют предоплату с доплатой при получении.

Чем отгрузка отличается от статуса заказа?

Отгрузка - объект со своими датами, складом и составом. Статус заказа - метка для менеджера, ничего сама по себе не меняющая.

Что такое служебная отгрузка?

Внутренняя запись, в которой лежат товары, пока не распределённые по настоящим отгрузкам. В расчётах её пропускают.

Где ловить факт оплаты для своей логики?

На событии смены признака оплаты. Оно срабатывает и при оплате через платёжную систему, и при отметке менеджером.

Смежное

Первоисточник