2026-08-22 21:29:53 +07:00
|
|
|
|
"""Долги: кто и сколько должен за взятые булки."""
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
|
|
from decimal import Decimal
|
|
|
|
|
|
|
|
|
|
|
|
from PySide6.QtCore import Qt
|
|
|
|
|
|
from PySide6.QtGui import QColor
|
|
|
|
|
|
from PySide6.QtWidgets import (
|
|
|
|
|
|
QDialog,
|
|
|
|
|
|
QFormLayout,
|
|
|
|
|
|
QHeaderView,
|
|
|
|
|
|
QLineEdit,
|
|
|
|
|
|
QMessageBox,
|
|
|
|
|
|
QTreeWidget,
|
|
|
|
|
|
QTreeWidgetItem,
|
|
|
|
|
|
QVBoxLayout,
|
|
|
|
|
|
QWidget,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
from .. import journal
|
|
|
|
|
|
from .. import money as m
|
|
|
|
|
|
from ..models import SALE_KIND_LABELS
|
Долг как явный выбор, история цен у фасовок
Долг было непонятно как записать, и не зря. Оплата была просто денежным
полем с полной суммой: долг получался, если догадаться стереть число
и вписать ноль. Дальше — хуже. Подсказка при этом продолжала уверять, что
оплачено полностью, а любая правка строк подставляла полную сумму обратно
и молча стирала выставленный долг.
Теперь выбор явный: заплатил полностью / взял в долг целиком / заплатил
часть. Пересчёт работает только в первом режиме. Поле суммы осталось
живым — вписанное руками число само зажигает подходящий переключатель.
Долг без имени записать нельзя: на экране долгов такая запись попадает
в кучу «без контрагента», и с кого спрашивать — уже не узнать. В быстром
вводе это предупреждение, а не запрет: внося историю, можно и правда не
помнить, кто это был.
На экране долгов человека можно выбрать целиком и рассчитаться сразу за
всё: деньги приходят одной суммой, а гасятся долги по очереди, со старых.
Цены фасовок переехали в «Цены и история». Они меняются одним решением
поставщика вместе с ценой товара, а история была только у товара —
у коробки цена уезжала молча. Заодно закрыт второй вход: в карточке
товара цены фасовок теперь только показываются, правится там состав.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 04:25:49 +07:00
|
|
|
|
from ..salesfilter import plural
|
2026-08-22 21:29:53 +07:00
|
|
|
|
from . import theme
|
|
|
|
|
|
from . import widgets as w
|
|
|
|
|
|
|
|
|
|
|
|
ROLE_SALE_ID = Qt.UserRole + 2
|
Долг как явный выбор, история цен у фасовок
Долг было непонятно как записать, и не зря. Оплата была просто денежным
полем с полной суммой: долг получался, если догадаться стереть число
и вписать ноль. Дальше — хуже. Подсказка при этом продолжала уверять, что
оплачено полностью, а любая правка строк подставляла полную сумму обратно
и молча стирала выставленный долг.
Теперь выбор явный: заплатил полностью / взял в долг целиком / заплатил
часть. Пересчёт работает только в первом режиме. Поле суммы осталось
живым — вписанное руками число само зажигает подходящий переключатель.
Долг без имени записать нельзя: на экране долгов такая запись попадает
в кучу «без контрагента», и с кого спрашивать — уже не узнать. В быстром
вводе это предупреждение, а не запрет: внося историю, можно и правда не
помнить, кто это был.
На экране долгов человека можно выбрать целиком и рассчитаться сразу за
всё: деньги приходят одной суммой, а гасятся долги по очереди, со старых.
Цены фасовок переехали в «Цены и история». Они меняются одним решением
поставщика вместе с ценой товара, а история была только у товара —
у коробки цена уезжала молча. Заодно закрыт второй вход: в карточке
товара цены фасовок теперь только показываются, правится там состав.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 04:25:49 +07:00
|
|
|
|
ROLE_COUNTERPARTY = Qt.UserRole + 3
|
2026-08-22 21:29:53 +07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PaymentDialog(QDialog):
|
Долг как явный выбор, история цен у фасовок
Долг было непонятно как записать, и не зря. Оплата была просто денежным
полем с полной суммой: долг получался, если догадаться стереть число
и вписать ноль. Дальше — хуже. Подсказка при этом продолжала уверять, что
оплачено полностью, а любая правка строк подставляла полную сумму обратно
и молча стирала выставленный долг.
Теперь выбор явный: заплатил полностью / взял в долг целиком / заплатил
часть. Пересчёт работает только в первом режиме. Поле суммы осталось
живым — вписанное руками число само зажигает подходящий переключатель.
Долг без имени записать нельзя: на экране долгов такая запись попадает
в кучу «без контрагента», и с кого спрашивать — уже не узнать. В быстром
вводе это предупреждение, а не запрет: внося историю, можно и правда не
помнить, кто это был.
На экране долгов человека можно выбрать целиком и рассчитаться сразу за
всё: деньги приходят одной суммой, а гасятся долги по очереди, со старых.
Цены фасовок переехали в «Цены и история». Они меняются одним решением
поставщика вместе с ценой товара, а история была только у товара —
у коробки цена уезжала молча. Заодно закрыт второй вход: в карточке
товара цены фасовок теперь только показываются, правится там состав.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 04:25:49 +07:00
|
|
|
|
def __init__(self, suggested: Decimal, currency: str, parent=None, what: str = "этой продаже"):
|
2026-08-22 21:29:53 +07:00
|
|
|
|
super().__init__(parent)
|
|
|
|
|
|
self.setWindowTitle("Принять оплату")
|
|
|
|
|
|
self.setMinimumWidth(340)
|
|
|
|
|
|
|
|
|
|
|
|
layout = QVBoxLayout(self)
|
|
|
|
|
|
layout.setContentsMargins(20, 16, 20, 16)
|
|
|
|
|
|
layout.setSpacing(10)
|
|
|
|
|
|
|
|
|
|
|
|
form = QFormLayout()
|
|
|
|
|
|
form.setSpacing(8)
|
|
|
|
|
|
|
|
|
|
|
|
self.date = w.DateInput()
|
|
|
|
|
|
form.addRow("Дата", self.date)
|
|
|
|
|
|
|
|
|
|
|
|
self.amount = w.MoneySpin(currency)
|
|
|
|
|
|
self.amount.set_decimal(suggested)
|
|
|
|
|
|
self.amount.selectAll()
|
|
|
|
|
|
form.addRow("Сумма", self.amount)
|
|
|
|
|
|
|
|
|
|
|
|
self.note = QLineEdit()
|
|
|
|
|
|
form.addRow("Заметка", self.note)
|
|
|
|
|
|
layout.addLayout(form)
|
|
|
|
|
|
|
Долг как явный выбор, история цен у фасовок
Долг было непонятно как записать, и не зря. Оплата была просто денежным
полем с полной суммой: долг получался, если догадаться стереть число
и вписать ноль. Дальше — хуже. Подсказка при этом продолжала уверять, что
оплачено полностью, а любая правка строк подставляла полную сумму обратно
и молча стирала выставленный долг.
Теперь выбор явный: заплатил полностью / взял в долг целиком / заплатил
часть. Пересчёт работает только в первом режиме. Поле суммы осталось
живым — вписанное руками число само зажигает подходящий переключатель.
Долг без имени записать нельзя: на экране долгов такая запись попадает
в кучу «без контрагента», и с кого спрашивать — уже не узнать. В быстром
вводе это предупреждение, а не запрет: внося историю, можно и правда не
помнить, кто это был.
На экране долгов человека можно выбрать целиком и рассчитаться сразу за
всё: деньги приходят одной суммой, а гасятся долги по очереди, со старых.
Цены фасовок переехали в «Цены и история». Они меняются одним решением
поставщика вместе с ценой товара, а история была только у товара —
у коробки цена уезжала молча. Заодно закрыт второй вход: в карточке
товара цены фасовок теперь только показываются, правится там состав.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 04:25:49 +07:00
|
|
|
|
self.hint = w.label(f"Долг по {what}: {m.fmt_money(suggested, currency)}", "dim")
|
|
|
|
|
|
self.hint.setWordWrap(True)
|
|
|
|
|
|
layout.addWidget(self.hint)
|
2026-08-22 21:29:53 +07:00
|
|
|
|
layout.addWidget(
|
|
|
|
|
|
w.row(None, w.button("Отмена", on_click=self.reject), w.button("Принять", "primary", self.accept))
|
|
|
|
|
|
)
|
|
|
|
|
|
self.amount.setFocus()
|
|
|
|
|
|
|
|
|
|
|
|
|
Долг как явный выбор, история цен у фасовок
Долг было непонятно как записать, и не зря. Оплата была просто денежным
полем с полной суммой: долг получался, если догадаться стереть число
и вписать ноль. Дальше — хуже. Подсказка при этом продолжала уверять, что
оплачено полностью, а любая правка строк подставляла полную сумму обратно
и молча стирала выставленный долг.
Теперь выбор явный: заплатил полностью / взял в долг целиком / заплатил
часть. Пересчёт работает только в первом режиме. Поле суммы осталось
живым — вписанное руками число само зажигает подходящий переключатель.
Долг без имени записать нельзя: на экране долгов такая запись попадает
в кучу «без контрагента», и с кого спрашивать — уже не узнать. В быстром
вводе это предупреждение, а не запрет: внося историю, можно и правда не
помнить, кто это был.
На экране долгов человека можно выбрать целиком и рассчитаться сразу за
всё: деньги приходят одной суммой, а гасятся долги по очереди, со старых.
Цены фасовок переехали в «Цены и история». Они меняются одним решением
поставщика вместе с ценой товара, а история была только у товара —
у коробки цена уезжала молча. Заодно закрыт второй вход: в карточке
товара цены фасовок теперь только показываются, правится там состав.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 04:25:49 +07:00
|
|
|
|
def ask_payment(parent, suggested: Decimal, currency: str, what: str = "этой продаже") -> Decimal | None:
|
2026-08-22 21:29:53 +07:00
|
|
|
|
"""Спросить сумму оплаты. Возвращает None, если отменили."""
|
Долг как явный выбор, история цен у фасовок
Долг было непонятно как записать, и не зря. Оплата была просто денежным
полем с полной суммой: долг получался, если догадаться стереть число
и вписать ноль. Дальше — хуже. Подсказка при этом продолжала уверять, что
оплачено полностью, а любая правка строк подставляла полную сумму обратно
и молча стирала выставленный долг.
Теперь выбор явный: заплатил полностью / взял в долг целиком / заплатил
часть. Пересчёт работает только в первом режиме. Поле суммы осталось
живым — вписанное руками число само зажигает подходящий переключатель.
Долг без имени записать нельзя: на экране долгов такая запись попадает
в кучу «без контрагента», и с кого спрашивать — уже не узнать. В быстром
вводе это предупреждение, а не запрет: внося историю, можно и правда не
помнить, кто это был.
На экране долгов человека можно выбрать целиком и рассчитаться сразу за
всё: деньги приходят одной суммой, а гасятся долги по очереди, со старых.
Цены фасовок переехали в «Цены и история». Они меняются одним решением
поставщика вместе с ценой товара, а история была только у товара —
у коробки цена уезжала молча. Заодно закрыт второй вход: в карточке
товара цены фасовок теперь только показываются, правится там состав.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 04:25:49 +07:00
|
|
|
|
dialog = PaymentDialog(suggested, currency, parent, what)
|
2026-08-22 21:29:53 +07:00
|
|
|
|
if dialog.exec() != QDialog.Accepted:
|
|
|
|
|
|
return None
|
|
|
|
|
|
return dialog.amount.value_decimal()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DebtsPage(QWidget):
|
|
|
|
|
|
"""Дерево: человек → его неоплаченные продажи."""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, ctx, parent=None):
|
|
|
|
|
|
super().__init__(parent)
|
|
|
|
|
|
self.ctx = ctx
|
|
|
|
|
|
|
|
|
|
|
|
layout = QVBoxLayout(self)
|
|
|
|
|
|
layout.setContentsMargins(20, 18, 20, 18)
|
|
|
|
|
|
layout.setSpacing(12)
|
|
|
|
|
|
|
|
|
|
|
|
layout.addWidget(w.heading("Долги"))
|
|
|
|
|
|
self.total_label = w.label("", "h2")
|
|
|
|
|
|
layout.addWidget(
|
|
|
|
|
|
w.row(
|
|
|
|
|
|
w.button("Принять оплату", "primary", self.take_payment),
|
|
|
|
|
|
w.button("Погасить целиком", on_click=self.settle_full),
|
|
|
|
|
|
None,
|
|
|
|
|
|
self.total_label,
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
Долг как явный выбор, история цен у фасовок
Долг было непонятно как записать, и не зря. Оплата была просто денежным
полем с полной суммой: долг получался, если догадаться стереть число
и вписать ноль. Дальше — хуже. Подсказка при этом продолжала уверять, что
оплачено полностью, а любая правка строк подставляла полную сумму обратно
и молча стирала выставленный долг.
Теперь выбор явный: заплатил полностью / взял в долг целиком / заплатил
часть. Пересчёт работает только в первом режиме. Поле суммы осталось
живым — вписанное руками число само зажигает подходящий переключатель.
Долг без имени записать нельзя: на экране долгов такая запись попадает
в кучу «без контрагента», и с кого спрашивать — уже не узнать. В быстром
вводе это предупреждение, а не запрет: внося историю, можно и правда не
помнить, кто это был.
На экране долгов человека можно выбрать целиком и рассчитаться сразу за
всё: деньги приходят одной суммой, а гасятся долги по очереди, со старых.
Цены фасовок переехали в «Цены и история». Они меняются одним решением
поставщика вместе с ценой товара, а история была только у товара —
у коробки цена уезжала молча. Заодно закрыт второй вход: в карточке
товара цены фасовок теперь только показываются, правится там состав.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 04:25:49 +07:00
|
|
|
|
layout.addWidget(
|
|
|
|
|
|
w.label(
|
|
|
|
|
|
"Выбери человека — рассчитаешься сразу за все его долги, начиная "
|
|
|
|
|
|
"со старых. Выбери отдельную продажу — оплата пойдёт только за неё.",
|
|
|
|
|
|
"dim",
|
|
|
|
|
|
)
|
|
|
|
|
|
)
|
2026-08-22 21:29:53 +07:00
|
|
|
|
|
|
|
|
|
|
self.tree = QTreeWidget()
|
|
|
|
|
|
self.tree.setHeaderLabels(["Кто / что", "Дата", "Сумма", "Оплачено", "Долг"])
|
|
|
|
|
|
self.tree.setAlternatingRowColors(True)
|
|
|
|
|
|
self.tree.setRootIsDecorated(True)
|
|
|
|
|
|
self.tree.header().setStretchLastSection(False)
|
|
|
|
|
|
self.tree.header().setSectionResizeMode(0, QHeaderView.Stretch)
|
|
|
|
|
|
self.tree.itemDoubleClicked.connect(self.take_payment)
|
|
|
|
|
|
layout.addWidget(self.tree, 1)
|
|
|
|
|
|
|
|
|
|
|
|
self.empty = w.label("Никто ничего не должен.", "dim")
|
|
|
|
|
|
layout.addWidget(self.empty)
|
|
|
|
|
|
|
|
|
|
|
|
# --- отрисовка ---
|
|
|
|
|
|
|
|
|
|
|
|
def refresh(self) -> None:
|
|
|
|
|
|
doc = self.ctx.vault.doc
|
|
|
|
|
|
currency = self.ctx.currency
|
|
|
|
|
|
debtors = self.ctx.report.debtors
|
|
|
|
|
|
|
|
|
|
|
|
self.tree.clear()
|
|
|
|
|
|
for debtor in debtors:
|
|
|
|
|
|
parent = QTreeWidgetItem(
|
|
|
|
|
|
[debtor.name, "", "", "", m.fmt_money(debtor.debt, currency)]
|
|
|
|
|
|
)
|
|
|
|
|
|
font = parent.font(0)
|
|
|
|
|
|
font.setBold(True)
|
|
|
|
|
|
parent.setFont(0, font)
|
|
|
|
|
|
parent.setFont(4, font)
|
|
|
|
|
|
parent.setForeground(4, QColor(theme.WARN))
|
|
|
|
|
|
parent.setTextAlignment(4, Qt.AlignRight | Qt.AlignVCenter)
|
Долг как явный выбор, история цен у фасовок
Долг было непонятно как записать, и не зря. Оплата была просто денежным
полем с полной суммой: долг получался, если догадаться стереть число
и вписать ноль. Дальше — хуже. Подсказка при этом продолжала уверять, что
оплачено полностью, а любая правка строк подставляла полную сумму обратно
и молча стирала выставленный долг.
Теперь выбор явный: заплатил полностью / взял в долг целиком / заплатил
часть. Пересчёт работает только в первом режиме. Поле суммы осталось
живым — вписанное руками число само зажигает подходящий переключатель.
Долг без имени записать нельзя: на экране долгов такая запись попадает
в кучу «без контрагента», и с кого спрашивать — уже не узнать. В быстром
вводе это предупреждение, а не запрет: внося историю, можно и правда не
помнить, кто это был.
На экране долгов человека можно выбрать целиком и рассчитаться сразу за
всё: деньги приходят одной суммой, а гасятся долги по очереди, со старых.
Цены фасовок переехали в «Цены и история». Они меняются одним решением
поставщика вместе с ценой товара, а история была только у товара —
у коробки цена уезжала молча. Заодно закрыт второй вход: в карточке
товара цены фасовок теперь только показываются, правится там состав.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 04:25:49 +07:00
|
|
|
|
# Человека можно выбрать целиком и рассчитаться сразу за всё:
|
|
|
|
|
|
# деньги приходят одной суммой, а не по продаже за раз.
|
|
|
|
|
|
parent.setData(0, ROLE_COUNTERPARTY, debtor.counterparty_id)
|
2026-08-22 21:29:53 +07:00
|
|
|
|
|
|
|
|
|
|
for sale in sorted(debtor.sales, key=lambda s: s.date):
|
|
|
|
|
|
what = ", ".join(
|
Отдельный репозиторий для базы, фасовки товара, ввод истории
Три правки по замечаниям.
1. База и код разъехались по разным репозиториям. Автокоммит и раньше
трогал только vault.fmdb, но лежал он в той же ветке, что и исходники,
поэтому пуш тащил всю историю программы. Теперь data/ — самостоятельный
клон food-records со своим .git, а исходники его игнорируют целиком.
При первом запуске приложение встаёт на уже существующую историю
сервера, а не заводит параллельную.
2. Фасовки товара. Пачка печенья 10 шт за 200 ₽ и та же печенька поштучно
за 30 ₽ — один товар с двумя фасовками. Остатки, себестоимость и FIFO
считаются в базовых единицах, поэтому поштучные продажи вычитаются из
купленных пачек. Размер фасовки хранится в документе слепком.
Миграция схемы 1→2 не меняет поведение уже заведённых данных.
3. Быстрый ввод продаж за период: выбор диапазона дат, строка таблицы —
отдельная продажа, ввод с клавиатуры, запись всех разом. Незнакомое имя
заводится как контрагент, повтор периода подсвечивается.
Тесты больше не имеют настроенного remote, чтобы не ходить в сеть.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 01:25:39 +07:00
|
|
|
|
f"{doc.product_name(line.product_id)} × {m.fmt_qty(line.qty)} "
|
|
|
|
|
|
f"{line.unit_name(doc.product(line.product_id))}"
|
2026-08-22 21:29:53 +07:00
|
|
|
|
for line in sale.lines
|
|
|
|
|
|
)
|
|
|
|
|
|
child = QTreeWidgetItem(
|
|
|
|
|
|
[
|
|
|
|
|
|
f"{SALE_KIND_LABELS.get(sale.kind, sale.kind)}: {what}",
|
|
|
|
|
|
sale.date.strftime("%d.%m.%Y"),
|
|
|
|
|
|
m.fmt_money(sale.total, currency),
|
|
|
|
|
|
m.fmt_money(sale.paid, currency),
|
|
|
|
|
|
m.fmt_money(sale.debt, currency),
|
|
|
|
|
|
]
|
|
|
|
|
|
)
|
|
|
|
|
|
child.setData(0, ROLE_SALE_ID, sale.id)
|
|
|
|
|
|
for column in (2, 3, 4):
|
|
|
|
|
|
child.setTextAlignment(column, Qt.AlignRight | Qt.AlignVCenter)
|
|
|
|
|
|
parent.addChild(child)
|
|
|
|
|
|
|
|
|
|
|
|
self.tree.addTopLevelItem(parent)
|
|
|
|
|
|
parent.setExpanded(True)
|
|
|
|
|
|
|
|
|
|
|
|
for column in range(1, 5):
|
|
|
|
|
|
self.tree.resizeColumnToContents(column)
|
|
|
|
|
|
|
|
|
|
|
|
total = self.ctx.report.summary.receivable
|
|
|
|
|
|
self.total_label.setText(f"Мне должны: {m.fmt_money(total, currency)}")
|
|
|
|
|
|
self.empty.setVisible(not debtors)
|
|
|
|
|
|
self.tree.setVisible(bool(debtors))
|
|
|
|
|
|
|
|
|
|
|
|
# --- действия ---
|
|
|
|
|
|
|
Долг как явный выбор, история цен у фасовок
Долг было непонятно как записать, и не зря. Оплата была просто денежным
полем с полной суммой: долг получался, если догадаться стереть число
и вписать ноль. Дальше — хуже. Подсказка при этом продолжала уверять, что
оплачено полностью, а любая правка строк подставляла полную сумму обратно
и молча стирала выставленный долг.
Теперь выбор явный: заплатил полностью / взял в долг целиком / заплатил
часть. Пересчёт работает только в первом режиме. Поле суммы осталось
живым — вписанное руками число само зажигает подходящий переключатель.
Долг без имени записать нельзя: на экране долгов такая запись попадает
в кучу «без контрагента», и с кого спрашивать — уже не узнать. В быстром
вводе это предупреждение, а не запрет: внося историю, можно и правда не
помнить, кто это был.
На экране долгов человека можно выбрать целиком и рассчитаться сразу за
всё: деньги приходят одной суммой, а гасятся долги по очереди, со старых.
Цены фасовок переехали в «Цены и история». Они меняются одним решением
поставщика вместе с ценой товара, а история была только у товара —
у коробки цена уезжала молча. Заодно закрыт второй вход: в карточке
товара цены фасовок теперь только показываются, правится там состав.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 04:25:49 +07:00
|
|
|
|
def _selected_debtor(self):
|
|
|
|
|
|
"""Выбранный человек и его общий долг — либо None, если выбрана продажа."""
|
|
|
|
|
|
item = self.tree.currentItem()
|
|
|
|
|
|
if item is None or item.parent() is not None:
|
|
|
|
|
|
return None
|
|
|
|
|
|
cp_id = item.data(0, ROLE_COUNTERPARTY)
|
|
|
|
|
|
return next(
|
|
|
|
|
|
(d for d in self.ctx.report.debtors if d.counterparty_id == cp_id), None
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-08-22 21:29:53 +07:00
|
|
|
|
def _selected_sale(self):
|
|
|
|
|
|
item = self.tree.currentItem()
|
|
|
|
|
|
if item is None:
|
Долг как явный выбор, история цен у фасовок
Долг было непонятно как записать, и не зря. Оплата была просто денежным
полем с полной суммой: долг получался, если догадаться стереть число
и вписать ноль. Дальше — хуже. Подсказка при этом продолжала уверять, что
оплачено полностью, а любая правка строк подставляла полную сумму обратно
и молча стирала выставленный долг.
Теперь выбор явный: заплатил полностью / взял в долг целиком / заплатил
часть. Пересчёт работает только в первом режиме. Поле суммы осталось
живым — вписанное руками число само зажигает подходящий переключатель.
Долг без имени записать нельзя: на экране долгов такая запись попадает
в кучу «без контрагента», и с кого спрашивать — уже не узнать. В быстром
вводе это предупреждение, а не запрет: внося историю, можно и правда не
помнить, кто это был.
На экране долгов человека можно выбрать целиком и рассчитаться сразу за
всё: деньги приходят одной суммой, а гасятся долги по очереди, со старых.
Цены фасовок переехали в «Цены и история». Они меняются одним решением
поставщика вместе с ценой товара, а история была только у товара —
у коробки цена уезжала молча. Заодно закрыт второй вход: в карточке
товара цены фасовок теперь только показываются, правится там состав.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 04:25:49 +07:00
|
|
|
|
QMessageBox.information(self, "Не выбрано", "Выбери человека или продажу.")
|
2026-08-22 21:29:53 +07:00
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
sale_id = item.data(0, ROLE_SALE_ID)
|
Долг как явный выбор, история цен у фасовок
Долг было непонятно как записать, и не зря. Оплата была просто денежным
полем с полной суммой: долг получался, если догадаться стереть число
и вписать ноль. Дальше — хуже. Подсказка при этом продолжала уверять, что
оплачено полностью, а любая правка строк подставляла полную сумму обратно
и молча стирала выставленный долг.
Теперь выбор явный: заплатил полностью / взял в долг целиком / заплатил
часть. Пересчёт работает только в первом режиме. Поле суммы осталось
живым — вписанное руками число само зажигает подходящий переключатель.
Долг без имени записать нельзя: на экране долгов такая запись попадает
в кучу «без контрагента», и с кого спрашивать — уже не узнать. В быстром
вводе это предупреждение, а не запрет: внося историю, можно и правда не
помнить, кто это был.
На экране долгов человека можно выбрать целиком и рассчитаться сразу за
всё: деньги приходят одной суммой, а гасятся долги по очереди, со старых.
Цены фасовок переехали в «Цены и история». Они меняются одним решением
поставщика вместе с ценой товара, а история была только у товара —
у коробки цена уезжала молча. Заодно закрыт второй вход: в карточке
товара цены фасовок теперь только показываются, правится там состав.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 04:25:49 +07:00
|
|
|
|
if not sale_id:
|
2026-08-22 21:29:53 +07:00
|
|
|
|
QMessageBox.information(
|
Долг как явный выбор, история цен у фасовок
Долг было непонятно как записать, и не зря. Оплата была просто денежным
полем с полной суммой: долг получался, если догадаться стереть число
и вписать ноль. Дальше — хуже. Подсказка при этом продолжала уверять, что
оплачено полностью, а любая правка строк подставляла полную сумму обратно
и молча стирала выставленный долг.
Теперь выбор явный: заплатил полностью / взял в долг целиком / заплатил
часть. Пересчёт работает только в первом режиме. Поле суммы осталось
живым — вписанное руками число само зажигает подходящий переключатель.
Долг без имени записать нельзя: на экране долгов такая запись попадает
в кучу «без контрагента», и с кого спрашивать — уже не узнать. В быстром
вводе это предупреждение, а не запрет: внося историю, можно и правда не
помнить, кто это был.
На экране долгов человека можно выбрать целиком и рассчитаться сразу за
всё: деньги приходят одной суммой, а гасятся долги по очереди, со старых.
Цены фасовок переехали в «Цены и история». Они меняются одним решением
поставщика вместе с ценой товара, а история была только у товара —
у коробки цена уезжала молча. Заодно закрыт второй вход: в карточке
товара цены фасовок теперь только показываются, правится там состав.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 04:25:49 +07:00
|
|
|
|
self, "Не выбрано", "Выбери человека или конкретную продажу."
|
2026-08-22 21:29:53 +07:00
|
|
|
|
)
|
|
|
|
|
|
return None
|
|
|
|
|
|
return self.ctx.vault.doc.sale(sale_id)
|
|
|
|
|
|
|
|
|
|
|
|
def take_payment(self) -> None:
|
Долг как явный выбор, история цен у фасовок
Долг было непонятно как записать, и не зря. Оплата была просто денежным
полем с полной суммой: долг получался, если догадаться стереть число
и вписать ноль. Дальше — хуже. Подсказка при этом продолжала уверять, что
оплачено полностью, а любая правка строк подставляла полную сумму обратно
и молча стирала выставленный долг.
Теперь выбор явный: заплатил полностью / взял в долг целиком / заплатил
часть. Пересчёт работает только в первом режиме. Поле суммы осталось
живым — вписанное руками число само зажигает подходящий переключатель.
Долг без имени записать нельзя: на экране долгов такая запись попадает
в кучу «без контрагента», и с кого спрашивать — уже не узнать. В быстром
вводе это предупреждение, а не запрет: внося историю, можно и правда не
помнить, кто это был.
На экране долгов человека можно выбрать целиком и рассчитаться сразу за
всё: деньги приходят одной суммой, а гасятся долги по очереди, со старых.
Цены фасовок переехали в «Цены и история». Они меняются одним решением
поставщика вместе с ценой товара, а история была только у товара —
у коробки цена уезжала молча. Заодно закрыт второй вход: в карточке
товара цены фасовок теперь только показываются, правится там состав.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 04:25:49 +07:00
|
|
|
|
debtor = self._selected_debtor()
|
|
|
|
|
|
if debtor is not None:
|
|
|
|
|
|
self._pay_debtor(debtor)
|
|
|
|
|
|
return
|
|
|
|
|
|
|
2026-08-22 21:29:53 +07:00
|
|
|
|
sale = self._selected_sale()
|
|
|
|
|
|
if sale is None:
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
amount = ask_payment(self, sale.debt, self.ctx.currency)
|
|
|
|
|
|
if amount is None:
|
|
|
|
|
|
return
|
|
|
|
|
|
try:
|
|
|
|
|
|
journal.add_sale_payment(self.ctx.vault, sale.id, amount)
|
|
|
|
|
|
except journal.ValidationError as exc:
|
|
|
|
|
|
QMessageBox.warning(self, "Не получилось", str(exc))
|
|
|
|
|
|
return
|
|
|
|
|
|
self.ctx.changed()
|
|
|
|
|
|
|
Долг как явный выбор, история цен у фасовок
Долг было непонятно как записать, и не зря. Оплата была просто денежным
полем с полной суммой: долг получался, если догадаться стереть число
и вписать ноль. Дальше — хуже. Подсказка при этом продолжала уверять, что
оплачено полностью, а любая правка строк подставляла полную сумму обратно
и молча стирала выставленный долг.
Теперь выбор явный: заплатил полностью / взял в долг целиком / заплатил
часть. Пересчёт работает только в первом режиме. Поле суммы осталось
живым — вписанное руками число само зажигает подходящий переключатель.
Долг без имени записать нельзя: на экране долгов такая запись попадает
в кучу «без контрагента», и с кого спрашивать — уже не узнать. В быстром
вводе это предупреждение, а не запрет: внося историю, можно и правда не
помнить, кто это был.
На экране долгов человека можно выбрать целиком и рассчитаться сразу за
всё: деньги приходят одной суммой, а гасятся долги по очереди, со старых.
Цены фасовок переехали в «Цены и история». Они меняются одним решением
поставщика вместе с ценой товара, а история была только у товара —
у коробки цена уезжала молча. Заодно закрыт второй вход: в карточке
товара цены фасовок теперь только показываются, правится там состав.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 04:25:49 +07:00
|
|
|
|
def _pay_debtor(self, debtor) -> None:
|
|
|
|
|
|
"""Принять деньги от человека сразу за всё, что за ним числится."""
|
|
|
|
|
|
amount = ask_payment(
|
|
|
|
|
|
self,
|
|
|
|
|
|
debtor.debt,
|
|
|
|
|
|
self.ctx.currency,
|
|
|
|
|
|
what=f"{debtor.name} — {len(debtor.sales)} "
|
|
|
|
|
|
+ plural(len(debtor.sales), "продажа", "продажи", "продаж"),
|
|
|
|
|
|
)
|
|
|
|
|
|
if amount is None:
|
|
|
|
|
|
return
|
|
|
|
|
|
try:
|
|
|
|
|
|
applied = journal.pay_off_counterparty(
|
|
|
|
|
|
self.ctx.vault, debtor.counterparty_id, amount
|
|
|
|
|
|
)
|
|
|
|
|
|
except journal.ValidationError as exc:
|
|
|
|
|
|
QMessageBox.warning(self, "Не получилось", str(exc))
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
self.ctx.changed()
|
|
|
|
|
|
if len(applied) > 1:
|
|
|
|
|
|
QMessageBox.information(
|
|
|
|
|
|
self,
|
|
|
|
|
|
"Готово",
|
|
|
|
|
|
f"{m.fmt_money(amount, self.ctx.currency)} разнесено "
|
|
|
|
|
|
f"по продажам: {len(applied)}, начиная со старых.",
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-08-22 21:29:53 +07:00
|
|
|
|
def settle_full(self) -> None:
|
Долг как явный выбор, история цен у фасовок
Долг было непонятно как записать, и не зря. Оплата была просто денежным
полем с полной суммой: долг получался, если догадаться стереть число
и вписать ноль. Дальше — хуже. Подсказка при этом продолжала уверять, что
оплачено полностью, а любая правка строк подставляла полную сумму обратно
и молча стирала выставленный долг.
Теперь выбор явный: заплатил полностью / взял в долг целиком / заплатил
часть. Пересчёт работает только в первом режиме. Поле суммы осталось
живым — вписанное руками число само зажигает подходящий переключатель.
Долг без имени записать нельзя: на экране долгов такая запись попадает
в кучу «без контрагента», и с кого спрашивать — уже не узнать. В быстром
вводе это предупреждение, а не запрет: внося историю, можно и правда не
помнить, кто это был.
На экране долгов человека можно выбрать целиком и рассчитаться сразу за
всё: деньги приходят одной суммой, а гасятся долги по очереди, со старых.
Цены фасовок переехали в «Цены и история». Они меняются одним решением
поставщика вместе с ценой товара, а история была только у товара —
у коробки цена уезжала молча. Заодно закрыт второй вход: в карточке
товара цены фасовок теперь только показываются, правится там состав.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 04:25:49 +07:00
|
|
|
|
debtor = self._selected_debtor()
|
|
|
|
|
|
if debtor is not None:
|
|
|
|
|
|
journal.pay_off_counterparty(
|
|
|
|
|
|
self.ctx.vault, debtor.counterparty_id, debtor.debt
|
|
|
|
|
|
)
|
|
|
|
|
|
self.ctx.changed()
|
2026-08-22 21:29:53 +07:00
|
|
|
|
return
|
Долг как явный выбор, история цен у фасовок
Долг было непонятно как записать, и не зря. Оплата была просто денежным
полем с полной суммой: долг получался, если догадаться стереть число
и вписать ноль. Дальше — хуже. Подсказка при этом продолжала уверять, что
оплачено полностью, а любая правка строк подставляла полную сумму обратно
и молча стирала выставленный долг.
Теперь выбор явный: заплатил полностью / взял в долг целиком / заплатил
часть. Пересчёт работает только в первом режиме. Поле суммы осталось
живым — вписанное руками число само зажигает подходящий переключатель.
Долг без имени записать нельзя: на экране долгов такая запись попадает
в кучу «без контрагента», и с кого спрашивать — уже не узнать. В быстром
вводе это предупреждение, а не запрет: внося историю, можно и правда не
помнить, кто это был.
На экране долгов человека можно выбрать целиком и рассчитаться сразу за
всё: деньги приходят одной суммой, а гасятся долги по очереди, со старых.
Цены фасовок переехали в «Цены и история». Они меняются одним решением
поставщика вместе с ценой товара, а история была только у товара —
у коробки цена уезжала молча. Заодно закрыт второй вход: в карточке
товара цены фасовок теперь только показываются, правится там состав.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 04:25:49 +07:00
|
|
|
|
|
|
|
|
|
|
sale = self._selected_sale()
|
|
|
|
|
|
if sale is None or sale.debt <= 0:
|
2026-08-22 21:29:53 +07:00
|
|
|
|
return
|
|
|
|
|
|
journal.add_sale_payment(self.ctx.vault, sale.id, sale.debt)
|
|
|
|
|
|
self.ctx.changed()
|