Commit Graph

9 Commits

Author SHA1 Message Date
Claude
3968806094 Пекарня может простить остаток
Надо было отдать 5 500, взяли 5 000, полтысячи оставили. Долг закрыт,
но денег никто не отдавал.

У расчёта с пекарней появился вид: деньги или прощённый остаток. Свалить
их в одну кучу нельзя — тогда «Отдано пекарне» в статистике показывало бы
суммы, которых не платил. Поэтому «Отдано» — только живые деньги,
«Прощено» — отдельная колонка везде: в закупках, в сводке, в статистике
по периодам и по времени.

В прибыль прощённое идёт целиком: эти деньги предназначались пекарне,
а остались у тебя. «Осталось собрать» на ту же сумму уменьшается —
собирать под прощённый остаток уже не надо. Простить больше долга нельзя:
лишнее не засчитывается и, в отличие от переплаты деньгами, никуда
не переливается.

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

Миграция схемы 3→4 помечает всё записанное раньше как живые деньги.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 02:44:19 +03:00
Claude
26b401e507 Цена по фасовке не подставлялась после удаления строки
Обработчики строк запоминали номер строки в момент создания. После
удаления любой строки всё, что было ниже, съезжает вверх, и запомненный
номер начинает указывать на соседа или за пределы таблицы. Строка молча
переставала работать целиком: выбираешь фасовку — цена не меняется,
меняешь товар — не пересобирается список фасовок. Приходилось править
цену руками.

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

Заодно найдена мина, которую посадил я сам в разделе статистики:
self.metric = QComboBox() на QWidget затеняет метод QWidget.metric(),
который Qt зовёт при смене стиля. Падало это в чужом месте и с невнятным
«object is not callable». Такая же история была с self.layout в сводке.
Оба переименованы, добавлен тест, который обходит все виджеты и проверяет,
что ни один атрибут не затеняет метод Qt.

И подсказка в карточке товара: себестоимость фасовки нигде не вводится,
она выводится из цены базовой единицы. Теперь под таблицей фасовок прямо
написано, какая сумма подставится в закупку.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 17:50:42 +03:00
Claude
38e9614a25 Раздел статистики
Шесть разрезов: периоды (заданные вручную, то есть партии), недели, месяцы,
годы, товары и люди. У каждого график и таблица с итоговой строкой.

Главное решение — две базы расчёта одновременно. Смешивать «когда продал»
и «когда получил деньги» нельзя: ответы расходятся, как только появляется
хоть один долг. Выручка, наценка, съеденное и чаевые считаются по дате
продажи, «Собрано» — по дате платежа, «Закуплено» и «Отдано пекарне» — по
своим датам. У разреза «Периоды» основание третье, привязка к партии,
потому что вопрос там другой: покрыл ли я её к сроку.

Количество нигде не сводится в одно число — только по единицам, «1,5 л ·
4 шт». Сумма литров с килограммами выглядит как настоящая цифра, но смысла
не имеет; ровно эту ошибку пришлось чинить и в разрезе по партиям.

График на QPainter: QtCharts исключён из сборки, он тянет десятки мегабайт
ради одной диаграммы. Убыток рисуется вниз от нулевой линии. Больше 24
корзин на график не помещается, и про срез написано над ним — молча
показанная часть выглядела бы как всё.

Тесты экранов больше не адресуют разделы по номеру в меню: новый раздел
ломал половину проверок.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 02:33:11 +03:00
Claude
582f041dba Чаевые
Деньги сверх стоимости товара. Хранятся на продаже отдельным полем, а не
внутри платежей: иначе чаевые раздували бы «оплачено», и продажа выглядела
бы закрытой, когда за булки ещё должны.

Ввёл оплату больше суммы — булка 70, дали 100 — разница уходит в чаевые
сама. Разбор делается по окончании ввода, а не на каждом нажатии, иначе
поле дёргалось бы посреди набора. То же при приёме оплаты по долгу:
заплатили больше, чем оставалось, — излишек становится чаевыми, а не
теряется, как было раньше.

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

По съеденному и подаренному чаевых не бывает — поле отключается.

Миграция схемы 2→3 проставляет ноль: у кого чаевых нет, тот появления
этой фичи не заметит.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 00:56:26 +03:00
Claude
4dd9a071c0 Починен ввод цены и выделение в таблицах
Цену было невозможно набрать. В прошлой правке я стал вырезать пробелы
из вводимого текста, чтобы принимались разделители разрядов, — но у поля
цены суффикс « ₽» начинается с пробела. Каждое нажатие ломало суффикс,
Qt отвергало символ, и в поле оставалась только первая цифра: набираешь
«25,50», получаешь 2.

Теперь текст на каждом нажатии не трогается вообще: сначала пробуем как
есть, и только если Qt отверг именно из-за точки — повторяем с запятой.

Второе: форма продажи подключала сигнал редактора строк раньше, чем
создавала поле оплаты. Qt глотает исключения в слотах, поэтому вместо
падения оплата просто молча не подставлялась.

Третье: выделение в таблицах держалось за номер строки. Таблицы
отсортированы, и после перерисовки под тем же номером оказывался другой
объект — можно было удалить не тот товар. Теперь выделение
восстанавливается по идентификатору.

Тесты: настоящие нажатия клавиш через QTest вместо проверок validate(),
которые этот баг пропустили, и перехват sys.excepthook, чтобы
проглоченные Qt исключения считались провалом.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 23:13:45 +03:00
Claude
bc868f5af1 Git-операции всегда над папкой данных, а не над корнем приложения
Приложение создавало GitSync от paths.app_root(). Пока база лежала внутри
репозитория с кодом, это работало; после разделения репозиториев — нет.
exe лежит в корне клона с исходниками, поэтому приложение распоряжалось
репозиторием кода: переписало ему origin на food-records и попыталось
отправить туда исходники. Пуш спас только отказ по fast-forward.

Теперь GitSync создаётся в одном месте — MainWindow._git() — и всегда
от paths.data_dir(). Тест проверяет и цель, и то, что других мест
создания GitSync в файле не осталось.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 22:30:42 +03:00
Claude
1a97fee872 Дробные количества: пол-литра сока, 1,125 кг сыра
Количества и раньше хранились в Decimal с тремя знаками, но до документа
они не доходили: поля ввода отвергали точку. Интерфейс русский, локаль
ждёт запятую, а на цифровой клавиатуре клавиша даёт точку — символ молча
не появлялся, и выглядело это так, будто дробные вводить нельзя вообще.
Теперь оба разделителя принимаются, пробелы-разделители разрядов тоже.

Заодно исправлена ошибка, которую дробные вскрыли: в сводке складывалось
общее количество остатка по всем товарам, то есть литры с килограммами
и штуками. Теперь остаток выражен деньгами и числом позиций, а разбивка
по единицам осталась в таблице «Остатки».

Фасовки тоже могут быть дробными: бутылка 1,5 л.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 22:20:29 +03:00
Claude
efce552e2b Отдельный репозиторий для базы, фасовки товара, ввод истории
Три правки по замечаниям.

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-22 21:25:39 +03:00
Claude
c8b9f5048b Учёт закупки и перепродажи булок
Десктопное приложение на PySide6. Вся база — один JSON-документ,
зашифрованный AES-256-GCM под паролем (ключ через scrypt), лежит
в data/vault.fmdb и раз в час уезжает в этот репозиторий.

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

Синхронизация коммитит только путь базы и схлопывает часовые пуши
в один коммит на день через amend + force-with-lease.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 17:29:53 +03:00