|
|
@@ -88,6 +88,19 @@
|
|
|
(format nil "~4,'0D-~2,'0D-~2,'0D ~2,'0D:~2,'0D:~2,'0D"
|
|
|
year month day hour min sec))))
|
|
|
|
|
|
+(defun format-date (ut)
|
|
|
+ (multiple-value-bind (sec min hour day month year)
|
|
|
+ (decode-universal-time ut)
|
|
|
+ (declare (ignore sec min hour))
|
|
|
+ (format nil "~4,'0D-~2,'0D-~2,'0D"
|
|
|
+ year month day)))
|
|
|
+
|
|
|
+(defun get-year (ut)
|
|
|
+ (multiple-value-bind (sec min hour day month year)
|
|
|
+ (decode-universal-time ut)
|
|
|
+ (declare (ignore sec min hour day month))
|
|
|
+ year))
|
|
|
+
|
|
|
(defun ledger/refresh-uri (chat-id uri)
|
|
|
(setf (gethash chat-id *ledger/chat-journals*)
|
|
|
(cons (pta-ledger:parse-journal (http-request uri))
|
|
|
@@ -147,6 +160,7 @@
|
|
|
`(let ((,info (get-chat-journal-info ,chat-id)))
|
|
|
(if ,info
|
|
|
(destructuring-bind (,journal . ,updated) ,info
|
|
|
+ (declare (ignorable ,journal ,updated))
|
|
|
,@body)
|
|
|
(send-response ,chat-id "Добавь журнал: uri - /ledger <url>; git - /ledger <remote> <path>")))))
|
|
|
|
|
|
@@ -192,22 +206,10 @@
|
|
|
|
|
|
;; New entries
|
|
|
(defun format-entry (entry)
|
|
|
- (format nil "```~%~A```" (pta-ledger:render entry)))
|
|
|
+ (let ((pta-ledger:*posting-length* 40))
|
|
|
+ (format nil "```~%~A```" (pta-ledger:render entry))))
|
|
|
|
|
|
-(defparameter +new-entry-actions+
|
|
|
- `(("a" . "➕")
|
|
|
- ("e" . "💲")
|
|
|
- ("c" . "✖️")))
|
|
|
|
|
|
-(defun ledger/new-chat-entry (chat-id entry)
|
|
|
- (bot-send-message chat-id (format-entry entry)
|
|
|
- :parse-mode "markdown"
|
|
|
- :reply-markup (telegram-inline-keyboard-markup
|
|
|
- (list (loop for (a . l) in +new-entry-actions+
|
|
|
- collect (list :text l
|
|
|
- :callback-data
|
|
|
- (encode-callback-data
|
|
|
- chat-id :ln a 86400)))))))
|
|
|
(defun ledger/format-add-entry-message (from)
|
|
|
(format nil "Ledger add from ~A ~A at ~A"
|
|
|
(aget "first_name" from) (aget "last_name" from)
|
|
|
@@ -229,13 +231,170 @@
|
|
|
(log:error "~A" e)
|
|
|
"Не смог :(")))
|
|
|
(:otherwise "Добавляю только в git журнал :("))
|
|
|
- "Добавь git журнал.")))
|
|
|
-
|
|
|
-(def-callback-section-handler cb-handle-ln (:ln)
|
|
|
- (case (keyify data)
|
|
|
- (:a (telegram-answer-callback-query query-id :text "Добавляю...")
|
|
|
- (telegram-send-message chat-id (ledger/process-add-entry
|
|
|
- chat-id callback))
|
|
|
- (telegram-edit-message-reply-markup nil :chat-id chat-id :message-id message-id))
|
|
|
- (:e (telegram-answer-callback-query query-id :text "TBD"))
|
|
|
- (:c (telegram-delete-message chat-id message-id))))
|
|
|
+ "Добавь git журнал.")))
|
|
|
+
|
|
|
+(defun ledger/new-chat-entry (chat-id entry)
|
|
|
+ (bot-send-message
|
|
|
+ chat-id (format-entry entry)
|
|
|
+ :parse-mode "markdown"
|
|
|
+ :reply-markup (keyboard/entry chat-id entry)))
|
|
|
+
|
|
|
+(defun entry-edit (chat-id message-id entry)
|
|
|
+ (telegram-edit-message-text
|
|
|
+ (format-entry entry)
|
|
|
+ :chat-id chat-id :message-id message-id
|
|
|
+ :parse-mode "markdown"
|
|
|
+ :reply-markup (keyboard/edit-entry chat-id entry)))
|
|
|
+
|
|
|
+(defun keyboard/entry (chat-id entry)
|
|
|
+ (get-inline-keyboard
|
|
|
+ chat-id
|
|
|
+ (list
|
|
|
+ (list (inline-button ("➕")
|
|
|
+ (telegram-answer-callback-query query-id :text "Добавляю...")
|
|
|
+ (telegram-send-message source-chat-id (ledger/process-add-entry
|
|
|
+ source-chat-id callback))
|
|
|
+ (telegram-edit-message-reply-markup
|
|
|
+ nil :chat-id source-chat-id :message-id source-message-id))
|
|
|
+ (inline-button ("💲")
|
|
|
+ (entry-edit source-chat-id source-message-id entry))
|
|
|
+ (inline-button ("✖️")
|
|
|
+ (telegram-delete-message source-chat-id source-message-id))))))
|
|
|
+
|
|
|
+(defun def (str default)
|
|
|
+ (if (or (null str) (string= "" str)) default
|
|
|
+ str))
|
|
|
+
|
|
|
+(defun keyboard/edit-entry (chat-id entry)
|
|
|
+ (get-inline-keyboard
|
|
|
+ chat-id
|
|
|
+ (append
|
|
|
+ (list
|
|
|
+ (list (inline-button ((format-date (pta-ledger:entry-date entry)))
|
|
|
+ (bot-send-message chat-id "Введите дату"
|
|
|
+ :reply-markup (telegram-force-reply))
|
|
|
+ (on-next-message chat-id
|
|
|
+ (let ((date (pta-ledger:parse-date text (get-year
|
|
|
+ (pta-ledger:entry-date entry)))))
|
|
|
+ (if date
|
|
|
+ (progn
|
|
|
+ (setf (pta-ledger:entry-date entry) date)
|
|
|
+ (entry-edit chat-id source-message-id entry))
|
|
|
+ (bot-send-message chat-id "Не разобрал")))))
|
|
|
+ (inline-button ((def (pta-ledger:entry-description entry) "Описание"))
|
|
|
+ (bot-send-message chat-id "Введите описание"
|
|
|
+ :reply-markup (telegram-force-reply))
|
|
|
+ (on-next-message chat-id
|
|
|
+ (setf (pta-ledger:entry-description entry) text)
|
|
|
+ (entry-edit chat-id source-message-id entry)))
|
|
|
+ (inline-button ((def (pta-ledger:entry-comment entry) "Коммент"))
|
|
|
+ (bot-send-message chat-id "Введите комментарий"
|
|
|
+ :reply-markup (telegram-force-reply))
|
|
|
+ (on-next-message chat-id
|
|
|
+ (setf (pta-ledger:entry-comment entry) text)
|
|
|
+ (entry-edit chat-id source-message-id entry)))))
|
|
|
+ (loop for posting in (pta-ledger:entry-postings entry)
|
|
|
+ collect
|
|
|
+ (let ((this-posting posting))
|
|
|
+ (list (inline-button ((pta-ledger:posting-account posting))
|
|
|
+ (account-edit chat-id source-message-id entry this-posting
|
|
|
+ (pta-ledger:posting-account this-posting)))
|
|
|
+ (inline-button ((def (pta-ledger:render
|
|
|
+ (pta-ledger:posting-amount posting))
|
|
|
+ "Сумма"))
|
|
|
+ (bot-send-message chat-id "Введите сумму"
|
|
|
+ :reply-markup (telegram-force-reply))
|
|
|
+ (on-next-message chat-id
|
|
|
+ (let ((amount (pta-ledger:parse-amount text)))
|
|
|
+ (setf (pta-ledger:posting-amount this-posting) amount)
|
|
|
+ (entry-edit chat-id source-message-id entry)))))))
|
|
|
+ (list (list (inline-button ("Готово")
|
|
|
+ (telegram-edit-message-reply-markup
|
|
|
+ (keyboard/entry chat-id entry)
|
|
|
+ :chat-id chat-id :message-id source-message-id)))))))
|
|
|
+
|
|
|
+(defun accounts/nav (account accounts &optional (offset 0) (count 5))
|
|
|
+ (let* ((len (1+ (length account)))
|
|
|
+ (sep-pos (position #\: account :from-end t))
|
|
|
+ (parent (when sep-pos (subseq account 0 sep-pos)))
|
|
|
+ (head (when account (concatenate 'string account ":")))
|
|
|
+ (descendants (remove-if #'(lambda (a)
|
|
|
+ (when head
|
|
|
+ (not (equal head
|
|
|
+ (subseq a 0 (min (length a) len))))))
|
|
|
+ accounts)))
|
|
|
+ (if descendants
|
|
|
+ (let* ((children (sort (remove-duplicates
|
|
|
+ (mapcar #'(lambda (d)
|
|
|
+ (subseq d 0 (or (position #\: d :start len)
|
|
|
+ (length d))))
|
|
|
+ descendants)
|
|
|
+ :test #'equal) #'string<))
|
|
|
+ (total (length children)))
|
|
|
+ (when (stringp offset)
|
|
|
+ (alexandria:when-let (off (position offset children :test #'equal))
|
|
|
+ (setf offset (max 0 (- off (round count 2))))))
|
|
|
+ (values account parent
|
|
|
+ (mapcar
|
|
|
+ #'(lambda (c)
|
|
|
+ (let* ((needle (concatenate 'string c ":"))
|
|
|
+ (n-l (length needle)))
|
|
|
+ (cons c
|
|
|
+ (not
|
|
|
+ (find needle accounts :test #'equal
|
|
|
+ :key #'(lambda (a)
|
|
|
+ (subseq a 0 (min n-l (length a)))))))))
|
|
|
+ (subseq children offset
|
|
|
+ (min total (+ offset count))))
|
|
|
+ (unless (zerop offset) (max 0 (- offset count)))
|
|
|
+ (when (< (+ offset count) total) (+ offset count))))
|
|
|
+ (when parent (accounts/nav parent accounts account)))))
|
|
|
+
|
|
|
+(defun account-edit (chat-id message-id entry posting account &optional (offset 0))
|
|
|
+ (with-chat-journal (chat-id journal updated)
|
|
|
+ (let* ((accounts (pta-ledger:journal-accounts journal))
|
|
|
+ (nav-list (multiple-value-list
|
|
|
+ (accounts/nav account accounts offset))))
|
|
|
+ (telegram-edit-message-reply-markup
|
|
|
+ (apply #'keyboard/account chat-id entry posting nav-list)
|
|
|
+ :chat-id chat-id :message-id message-id))))
|
|
|
+
|
|
|
+(defun keyboard/account (chat-id entry posting account parent children prev next)
|
|
|
+ (get-inline-keyboard
|
|
|
+ chat-id
|
|
|
+ (append
|
|
|
+ (list (list (when account
|
|
|
+ (inline-button (account)
|
|
|
+ (setf (pta-ledger:posting-account posting) account)
|
|
|
+ (entry-edit chat-id source-message-id entry)))
|
|
|
+ (inline-button ("Ввести")
|
|
|
+ (bot-send-message chat-id "Введите счёт"
|
|
|
+ :reply-markup (telegram-force-reply))
|
|
|
+ (on-next-message chat-id
|
|
|
+ (let ((account (pta-ledger:parse-account text)))
|
|
|
+ (if account
|
|
|
+ (progn
|
|
|
+ (setf (pta-ledger:posting-account posting) account)
|
|
|
+ (entry-edit chat-id source-message-id entry))
|
|
|
+ (bot-send-message chat-id "Не разобрал")))))))
|
|
|
+ (loop for (acc . leaf) in children
|
|
|
+ collect (let ((this-account acc))
|
|
|
+ (list (if leaf
|
|
|
+ (inline-button (this-account)
|
|
|
+ (setf (pta-ledger:posting-account posting) this-account)
|
|
|
+ (entry-edit chat-id source-message-id entry))
|
|
|
+ (inline-button ((format nil "~A ..." this-account))
|
|
|
+ (account-edit chat-id source-message-id entry posting
|
|
|
+ this-account))))))
|
|
|
+ (list (list (when prev
|
|
|
+ (inline-button ("<<")
|
|
|
+ (account-edit chat-id source-message-id entry posting
|
|
|
+ account prev)))
|
|
|
+ (when (or parent account)
|
|
|
+ (inline-button ((or parent "ACC"))
|
|
|
+ (account-edit chat-id source-message-id entry posting
|
|
|
+ parent account)))
|
|
|
+ (when next
|
|
|
+ (inline-button (">>")
|
|
|
+ (account-edit chat-id source-message-id entry posting
|
|
|
+ account next))))))))
|